diff --git a/src/components/Settings/index.tsx b/src/components/Settings/index.tsx
index 7943a50..a1444dd 100644
--- a/src/components/Settings/index.tsx
+++ b/src/components/Settings/index.tsx
@@ -129,6 +129,13 @@ export default function Settings() {
{copiedNcryptsec ? : }
)}
+ push(toSystemSettings())}>
+
+
+
@@ -143,13 +150,6 @@ export default function Settings() {
- push(toSystemSettings())}>
-
-
-
diff --git a/src/constants.ts b/src/constants.ts
index 40076a9..7ab4cc2 100644
--- a/src/constants.ts
+++ b/src/constants.ts
@@ -40,6 +40,7 @@ export const StorageKey = {
PRIMARY_COLOR: 'primaryColor',
ENABLE_SINGLE_COLUMN_LAYOUT: 'enableSingleColumnLayout',
FAVICON_URL_TEMPLATE: 'faviconUrlTemplate',
+ FILTER_OUT_ONION_RELAYS: 'filterOutOnionRelays',
MEDIA_UPLOAD_SERVICE: 'mediaUploadService', // deprecated
HIDE_UNTRUSTED_EVENTS: 'hideUntrustedEvents', // deprecated
ACCOUNT_RELAY_LIST_EVENT_MAP: 'accountRelayListEventMap', // deprecated
diff --git a/src/i18n/locales/ar.ts b/src/i18n/locales/ar.ts
index f866f4f..fcd359f 100644
--- a/src/i18n/locales/ar.ts
+++ b/src/i18n/locales/ar.ts
@@ -532,6 +532,7 @@ export default {
'Failed to get invite code from relay': 'فشل الحصول على رمز الدعوة من المرحل',
'Failed to get invite code': 'فشل الحصول على رمز الدعوة',
'Invite code copied to clipboard': 'تم نسخ رمز الدعوة إلى الحافظة',
- 'Favicon URL': 'رابط الأيقونة المفضلة'
+ 'Favicon URL': 'رابط الأيقونة المفضلة',
+ 'Filter out onion relays': 'تصفية مرحلات onion'
}
}
diff --git a/src/i18n/locales/de.ts b/src/i18n/locales/de.ts
index ca8720a..0ff71ab 100644
--- a/src/i18n/locales/de.ts
+++ b/src/i18n/locales/de.ts
@@ -548,6 +548,7 @@ export default {
'Failed to get invite code from relay': 'Fehler beim Abrufen des Einladungscodes vom Relay',
'Failed to get invite code': 'Fehler beim Abrufen des Einladungscodes',
'Invite code copied to clipboard': 'Einladungscode in die Zwischenablage kopiert',
- 'Favicon URL': 'Favicon-URL'
+ 'Favicon URL': 'Favicon-URL',
+ 'Filter out onion relays': 'Onion-Relays herausfiltern'
}
}
diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts
index 6b6f93c..e711503 100644
--- a/src/i18n/locales/en.ts
+++ b/src/i18n/locales/en.ts
@@ -533,6 +533,7 @@ export default {
'Failed to get invite code from relay': 'Failed to get invite code from relay',
'Failed to get invite code': 'Failed to get invite code',
'Invite code copied to clipboard': 'Invite code copied to clipboard',
- 'Favicon URL': 'Favicon URL'
+ 'Favicon URL': 'Favicon URL',
+ 'Filter out onion relays': 'Filter out onion relays'
}
}
diff --git a/src/i18n/locales/es.ts b/src/i18n/locales/es.ts
index 421288e..74d92e8 100644
--- a/src/i18n/locales/es.ts
+++ b/src/i18n/locales/es.ts
@@ -542,6 +542,7 @@ export default {
'Failed to get invite code from relay': 'Error al obtener código de invitación del relay',
'Failed to get invite code': 'Error al obtener código de invitación',
'Invite code copied to clipboard': 'Código de invitación copiado al portapapeles',
- 'Favicon URL': 'URL del Favicon'
+ 'Favicon URL': 'URL del Favicon',
+ 'Filter out onion relays': 'Filtrar relés onion'
}
}
diff --git a/src/i18n/locales/fa.ts b/src/i18n/locales/fa.ts
index 7762659..ba9f349 100644
--- a/src/i18n/locales/fa.ts
+++ b/src/i18n/locales/fa.ts
@@ -537,6 +537,7 @@ export default {
'Failed to get invite code from relay': 'دریافت کد دعوت از رله ناموفق بود',
'Failed to get invite code': 'دریافت کد دعوت ناموفق بود',
'Invite code copied to clipboard': 'کد دعوت در کلیپبورد کپی شد',
- 'Favicon URL': 'آدرس نماد سایت'
+ 'Favicon URL': 'آدرس نماد سایت',
+ 'Filter out onion relays': 'فیلتر کردن رلههای onion'
}
}
diff --git a/src/i18n/locales/fr.ts b/src/i18n/locales/fr.ts
index b1ef862..d776a1b 100644
--- a/src/i18n/locales/fr.ts
+++ b/src/i18n/locales/fr.ts
@@ -547,6 +547,7 @@ export default {
'Failed to get invite code from relay': "Échec de l'obtention du code d'invitation du relay",
'Failed to get invite code': "Échec de l'obtention du code d'invitation",
'Invite code copied to clipboard': "Code d'invitation copié dans le presse-papiers",
- 'Favicon URL': 'URL du Favicon'
+ 'Favicon URL': 'URL du Favicon',
+ 'Filter out onion relays': 'Filtrer les relais onion'
}
}
diff --git a/src/i18n/locales/hi.ts b/src/i18n/locales/hi.ts
index 0e70d60..0f22d71 100644
--- a/src/i18n/locales/hi.ts
+++ b/src/i18n/locales/hi.ts
@@ -539,6 +539,7 @@ export default {
'Failed to get invite code from relay': 'रिले से निमंत्रण कोड प्राप्त करने में विफल',
'Failed to get invite code': 'निमंत्रण कोड प्राप्त करने में विफल',
'Invite code copied to clipboard': 'निमंत्रण कोड क्लिपबोर्ड पर कॉपी किया गया',
- 'Favicon URL': 'फ़ेविकॉन URL'
+ 'Favicon URL': 'फ़ेविकॉन URL',
+ 'Filter out onion relays': 'ओनियन रिले फ़िल्टर करें'
}
}
diff --git a/src/i18n/locales/hu.ts b/src/i18n/locales/hu.ts
index dd4457e..409f04c 100644
--- a/src/i18n/locales/hu.ts
+++ b/src/i18n/locales/hu.ts
@@ -534,6 +534,7 @@ export default {
'Failed to get invite code from relay': 'Nem sikerült lekérni a meghívókódot a relay-től',
'Failed to get invite code': 'Nem sikerült lekérni a meghívókódot',
'Invite code copied to clipboard': 'Meghívókód vágólapra másolva',
- 'Favicon URL': 'Favicon URL'
+ 'Favicon URL': 'Favicon URL',
+ 'Filter out onion relays': 'Onion relay-ek kiszűrése'
}
}
diff --git a/src/i18n/locales/it.ts b/src/i18n/locales/it.ts
index 10ac08f..7cf0d17 100644
--- a/src/i18n/locales/it.ts
+++ b/src/i18n/locales/it.ts
@@ -542,6 +542,7 @@ export default {
'Failed to get invite code from relay': 'Impossibile ottenere il codice di invito dal relay',
'Failed to get invite code': 'Impossibile ottenere il codice di invito',
'Invite code copied to clipboard': 'Codice di invito copiato negli appunti',
- 'Favicon URL': 'URL Favicon'
+ 'Favicon URL': 'URL Favicon',
+ 'Filter out onion relays': 'Filtra relay onion'
}
}
diff --git a/src/i18n/locales/ja.ts b/src/i18n/locales/ja.ts
index eed9853..9a49546 100644
--- a/src/i18n/locales/ja.ts
+++ b/src/i18n/locales/ja.ts
@@ -536,6 +536,7 @@ export default {
'Failed to get invite code from relay': 'リレーから招待コードの取得に失敗しました',
'Failed to get invite code': '招待コードの取得に失敗しました',
'Invite code copied to clipboard': '招待コードをクリップボードにコピーしました',
- 'Favicon URL': 'ファビコンURL'
+ 'Favicon URL': 'ファビコンURL',
+ 'Filter out onion relays': 'Onionリレーを除外'
}
}
diff --git a/src/i18n/locales/ko.ts b/src/i18n/locales/ko.ts
index 499f7f9..2a3c735 100644
--- a/src/i18n/locales/ko.ts
+++ b/src/i18n/locales/ko.ts
@@ -536,6 +536,7 @@ export default {
'Failed to get invite code from relay': '릴레이에서 초대 코드 가져오기 실패',
'Failed to get invite code': '초대 코드 가져오기 실패',
'Invite code copied to clipboard': '초대 코드가 클립보드에 복사되었습니다',
- 'Favicon URL': '파비콘 URL'
+ 'Favicon URL': '파비콘 URL',
+ 'Filter out onion relays': '어니언 릴레이 필터링'
}
}
diff --git a/src/i18n/locales/pl.ts b/src/i18n/locales/pl.ts
index 1181c03..f40de91 100644
--- a/src/i18n/locales/pl.ts
+++ b/src/i18n/locales/pl.ts
@@ -542,6 +542,7 @@ export default {
'Failed to get invite code from relay': 'Nie udało się uzyskać kodu zaproszenia z przekaźnika',
'Failed to get invite code': 'Nie udało się uzyskać kodu zaproszenia',
'Invite code copied to clipboard': 'Kod zaproszenia skopiowany do schowka',
- 'Favicon URL': 'URL Favicon'
+ 'Favicon URL': 'URL Favicon',
+ 'Filter out onion relays': 'Filtruj przekaźniki onion'
}
}
diff --git a/src/i18n/locales/pt-BR.ts b/src/i18n/locales/pt-BR.ts
index 881f229..7d7edcf 100644
--- a/src/i18n/locales/pt-BR.ts
+++ b/src/i18n/locales/pt-BR.ts
@@ -539,6 +539,7 @@ export default {
'Failed to get invite code from relay': 'Falha ao obter código de convite do relay',
'Failed to get invite code': 'Falha ao obter código de convite',
'Invite code copied to clipboard': 'Código de convite copiado para a área de transferência',
- 'Favicon URL': 'URL do Favicon'
+ 'Favicon URL': 'URL do Favicon',
+ 'Filter out onion relays': 'Filtrar relays onion'
}
}
diff --git a/src/i18n/locales/pt-PT.ts b/src/i18n/locales/pt-PT.ts
index f643deb..74419f4 100644
--- a/src/i18n/locales/pt-PT.ts
+++ b/src/i18n/locales/pt-PT.ts
@@ -542,6 +542,7 @@ export default {
'Failed to get invite code from relay': 'Falha ao obter código de convite do relay',
'Failed to get invite code': 'Falha ao obter código de convite',
'Invite code copied to clipboard': 'Código de convite copiado para a área de transferência',
- 'Favicon URL': 'URL do Favicon'
+ 'Favicon URL': 'URL do Favicon',
+ 'Filter out onion relays': 'Filtrar relays onion'
}
}
diff --git a/src/i18n/locales/ru.ts b/src/i18n/locales/ru.ts
index b530d18..f5457ad 100644
--- a/src/i18n/locales/ru.ts
+++ b/src/i18n/locales/ru.ts
@@ -544,6 +544,7 @@ export default {
'Failed to get invite code from relay': 'Не удалось получить код приглашения от релея',
'Failed to get invite code': 'Не удалось получить код приглашения',
'Invite code copied to clipboard': 'Код приглашения скопирован в буфер обмена',
- 'Favicon URL': 'URL фавикона'
+ 'Favicon URL': 'URL фавикона',
+ 'Filter out onion relays': 'Фильтровать onion-релеи'
}
}
diff --git a/src/i18n/locales/th.ts b/src/i18n/locales/th.ts
index d9502e5..5c78ee9 100644
--- a/src/i18n/locales/th.ts
+++ b/src/i18n/locales/th.ts
@@ -530,6 +530,7 @@ export default {
'Failed to get invite code from relay': 'ไม่สามารถรับรหัสเชิญจากรีเลย์',
'Failed to get invite code': 'ไม่สามารถรับรหัสเชิญ',
'Invite code copied to clipboard': 'คัดลอกรหัสเชิญไปยังคลิปบอร์ดแล้ว',
- 'Favicon URL': 'URL ไอคอน'
+ 'Favicon URL': 'URL ไอคอน',
+ 'Filter out onion relays': 'กรองรีเลย์ onion'
}
}
diff --git a/src/i18n/locales/zh.ts b/src/i18n/locales/zh.ts
index 7e2bfd0..a6343c1 100644
--- a/src/i18n/locales/zh.ts
+++ b/src/i18n/locales/zh.ts
@@ -527,6 +527,7 @@ export default {
'Failed to get invite code from relay': '从中继器获取邀请码失败',
'Failed to get invite code': '获取邀请码失败',
'Invite code copied to clipboard': '邀请码已复制到剪贴板',
- 'Favicon URL': '网站图标 URL'
+ 'Favicon URL': '网站图标 URL',
+ 'Filter out onion relays': '过滤洋葱中继'
}
}
diff --git a/src/lib/event-metadata.ts b/src/lib/event-metadata.ts
index e667bc7..e02c8e5 100644
--- a/src/lib/event-metadata.ts
+++ b/src/lib/event-metadata.ts
@@ -5,16 +5,17 @@ import { buildATag } from './draft-event'
import { getReplaceableEventIdentifier } from './event'
import { getAmountFromInvoice, getLightningAddressFromProfile } from './lightning'
import { formatPubkey, pubkeyToNpub } from './pubkey'
-import { getEmojiInfosFromEmojiTags, generateBech32IdFromETag, tagNameEquals } from './tag'
-import { isWebsocketUrl, normalizeHttpUrl, normalizeUrl } from './url'
-import { isTorBrowser } from './utils'
+import { generateBech32IdFromETag, getEmojiInfosFromEmojiTags, tagNameEquals } from './tag'
+import { isOnionUrl, isWebsocketUrl, normalizeHttpUrl, normalizeUrl } from './url'
-export function getRelayListFromEvent(event?: Event | null) {
+export function getRelayListFromEvent(
+ event?: Event | null,
+ filterOutOnionRelays: boolean = true
+): TRelayList {
if (!event) {
return { write: BIG_RELAY_URLS, read: BIG_RELAY_URLS, originalRelays: [] }
}
- const torBrowserDetected = isTorBrowser()
const relayList = { write: [], read: [], originalRelays: [] } as TRelayList
event.tags.filter(tagNameEquals('r')).forEach(([, url, type]) => {
if (!url || !isWebsocketUrl(url)) return
@@ -25,8 +26,7 @@ export function getRelayListFromEvent(event?: Event | null) {
const scope = type === 'read' ? 'read' : type === 'write' ? 'write' : 'both'
relayList.originalRelays.push({ url: normalizedUrl, scope })
- // Filter out .onion URLs if not using Tor browser
- if (normalizedUrl.endsWith('.onion/') && !torBrowserDetected) return
+ if (filterOutOnionRelays && isOnionUrl(normalizedUrl)) return
if (type === 'write') {
relayList.write.push(normalizedUrl)
diff --git a/src/lib/url.ts b/src/lib/url.ts
index 9676dbc..9218b36 100644
--- a/src/lib/url.ts
+++ b/src/lib/url.ts
@@ -2,6 +2,15 @@ export function isWebsocketUrl(url: string): boolean {
return /^wss?:\/\/.+$/.test(url)
}
+export function isOnionUrl(url: string): boolean {
+ try {
+ const hostname = new URL(url).hostname
+ return hostname.endsWith('.onion')
+ } catch {
+ return false
+ }
+}
+
// copy from nostr-tools/utils
export function normalizeUrl(url: string): string {
try {
diff --git a/src/pages/secondary/SystemSettingsPage/index.tsx b/src/pages/secondary/SystemSettingsPage/index.tsx
index 9a71add..8a949c5 100644
--- a/src/pages/secondary/SystemSettingsPage/index.tsx
+++ b/src/pages/secondary/SystemSettingsPage/index.tsx
@@ -1,14 +1,19 @@
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
+import { Switch } from '@/components/ui/switch'
import { DEFAULT_FAVICON_URL_TEMPLATE } from '@/constants'
import SecondaryPageLayout from '@/layouts/SecondaryPageLayout'
import { useContentPolicy } from '@/providers/ContentPolicyProvider'
-import { forwardRef } from 'react'
+import storage from '@/services/local-storage.service'
+import { forwardRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
const SystemSettingsPage = forwardRef(({ index }: { index?: number }, ref) => {
const { t } = useTranslation()
const { faviconUrlTemplate, setFaviconUrlTemplate } = useContentPolicy()
+ const [filterOutOnionRelays, setFilterOutOnionRelays] = useState(
+ storage.getFilterOutOnionRelays()
+ )
return (
@@ -25,6 +30,19 @@ const SystemSettingsPage = forwardRef(({ index }: { index?: number }, ref) => {
placeholder={DEFAULT_FAVICON_URL_TEMPLATE}
/>
+
+
+ {
+ storage.setFilterOutOnionRelays(checked)
+ setFilterOutOnionRelays(checked)
+ }}
+ />
+
)
diff --git a/src/providers/NostrProvider/index.tsx b/src/providers/NostrProvider/index.tsx
index 01cd1f3..47bf336 100644
--- a/src/providers/NostrProvider/index.tsx
+++ b/src/providers/NostrProvider/index.tsx
@@ -207,7 +207,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
indexedDb.getReplaceableEvent(account.pubkey, kinds.Pinlist)
])
if (storedRelayListEvent) {
- setRelayList(getRelayListFromEvent(storedRelayListEvent))
+ setRelayList(getRelayListFromEvent(storedRelayListEvent, storage.getFilterOutOnionRelays()))
}
if (storedProfileEvent) {
setProfileEvent(storedProfileEvent)
@@ -237,7 +237,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
authors: [account.pubkey]
})
const relayListEvent = getLatestEvent(relayListEvents) ?? storedRelayListEvent
- const relayList = getRelayListFromEvent(relayListEvent)
+ const relayList = getRelayListFromEvent(relayListEvent, storage.getFilterOutOnionRelays())
if (relayListEvent) {
client.updateRelayListCache(relayListEvent)
await indexedDb.putReplaceableEvent(relayListEvent)
@@ -705,7 +705,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
const updateRelayListEvent = async (relayListEvent: Event) => {
const newRelayList = await client.updateRelayListCache(relayListEvent)
- setRelayList(getRelayListFromEvent(newRelayList))
+ setRelayList(getRelayListFromEvent(newRelayList, storage.getFilterOutOnionRelays()))
}
const updateProfileEvent = async (profileEvent: Event) => {
diff --git a/src/services/client.service.ts b/src/services/client.service.ts
index 13a5aa8..6be6796 100644
--- a/src/services/client.service.ts
+++ b/src/services/client.service.ts
@@ -31,6 +31,7 @@ import {
} from 'nostr-tools'
import { AbstractRelay } from 'nostr-tools/abstract-relay'
import indexedDb from './indexed-db.service'
+import storage from './local-storage.service'
type TTimelineRef = [string, number]
@@ -1131,7 +1132,7 @@ class ClientService extends EventTarget {
return relayEvents.map((event) => {
if (event) {
- return getRelayListFromEvent(event)
+ return getRelayListFromEvent(event, storage.getFilterOutOnionRelays())
}
return {
write: BIG_RELAY_URLS,
diff --git a/src/services/local-storage.service.ts b/src/services/local-storage.service.ts
index a3d2e11..82ebe43 100644
--- a/src/services/local-storage.service.ts
+++ b/src/services/local-storage.service.ts
@@ -10,6 +10,7 @@ import {
} from '@/constants'
import { isSameAccount } from '@/lib/account'
import { randomString } from '@/lib/random'
+import { isTorBrowser } from '@/lib/utils'
import {
TAccount,
TAccountPointer,
@@ -54,6 +55,7 @@ class LocalStorageService {
private primaryColor: TPrimaryColor = 'DEFAULT'
private enableSingleColumnLayout: boolean = true
private faviconUrlTemplate: string = DEFAULT_FAVICON_URL_TEMPLATE
+ private filterOutOnionRelays: boolean = !isTorBrowser()
constructor() {
if (!LocalStorageService.instance) {
@@ -210,6 +212,11 @@ class LocalStorageService {
this.faviconUrlTemplate =
window.localStorage.getItem(StorageKey.FAVICON_URL_TEMPLATE) ?? DEFAULT_FAVICON_URL_TEMPLATE
+ const filterOutOnionRelaysStr = window.localStorage.getItem(StorageKey.FILTER_OUT_ONION_RELAYS)
+ if (filterOutOnionRelaysStr) {
+ this.filterOutOnionRelays = filterOutOnionRelaysStr !== 'false'
+ }
+
// Clean up deprecated data
window.localStorage.removeItem(StorageKey.ACCOUNT_PROFILE_EVENT_MAP)
window.localStorage.removeItem(StorageKey.ACCOUNT_FOLLOW_LIST_EVENT_MAP)
@@ -529,6 +536,15 @@ class LocalStorageService {
this.faviconUrlTemplate = template
window.localStorage.setItem(StorageKey.FAVICON_URL_TEMPLATE, template)
}
+
+ getFilterOutOnionRelays() {
+ return this.filterOutOnionRelays
+ }
+
+ setFilterOutOnionRelays(filterOut: boolean) {
+ this.filterOutOnionRelays = filterOut
+ window.localStorage.setItem(StorageKey.FILTER_OUT_ONION_RELAYS, filterOut.toString())
+ }
}
const instance = new LocalStorageService()