diff --git a/src/App.tsx b/src/App.tsx
index d88f1b5..9d341a4 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -4,6 +4,7 @@ import './index.css'
import { Toaster } from '@/components/ui/sonner'
import { BookmarksProvider } from '@/providers/BookmarksProvider'
import { ContentPolicyProvider } from '@/providers/ContentPolicyProvider'
+import { DeletedEventProvider } from '@/providers/DeletedEventProvider'
import { FavoriteRelaysProvider } from '@/providers/FavoriteRelaysProvider'
import { FeedProvider } from '@/providers/FeedProvider'
import { FollowListProvider } from '@/providers/FollowListProvider'
@@ -24,32 +25,34 @@ export default function App(): JSX.Element {
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/components/NoteList/index.tsx b/src/components/NoteList/index.tsx
index a4bbc68..a4448fb 100644
--- a/src/components/NoteList/index.tsx
+++ b/src/components/NoteList/index.tsx
@@ -5,6 +5,7 @@ import {
isReplaceableEvent,
isReplyNoteEvent
} from '@/lib/event'
+import { useDeletedEvent } from '@/providers/DeletedEventProvider'
import { useMuteList } from '@/providers/MuteListProvider'
import { useNostr } from '@/providers/NostrProvider'
import { useUserTrust } from '@/providers/UserTrustProvider'
@@ -44,6 +45,7 @@ const NoteList = forwardRef(
const { startLogin } = useNostr()
const { isUserTrusted } = useUserTrust()
const { mutePubkeys } = useMuteList()
+ const { isEventDeleted } = useDeletedEvent()
const [events, setEvents] = useState([])
const [newEvents, setNewEvents] = useState([])
const [hasMore, setHasMore] = useState(true)
@@ -58,6 +60,7 @@ const NoteList = forwardRef(
const idSet = new Set()
return events.slice(0, showCount).filter((evt) => {
+ if (isEventDeleted(evt)) return false
if (hideReplies && isReplyNoteEvent(evt)) return false
if (hideUntrustedNotes && !isUserTrusted(evt.pubkey)) return false
@@ -68,12 +71,13 @@ const NoteList = forwardRef(
idSet.add(id)
return true
})
- }, [events, hideReplies, hideUntrustedNotes, showCount])
+ }, [events, hideReplies, hideUntrustedNotes, showCount, isEventDeleted])
const filteredNewEvents = useMemo(() => {
const idSet = new Set()
return newEvents.filter((event: Event) => {
+ if (isEventDeleted(event)) return false
if (hideReplies && isReplyNoteEvent(event)) return false
if (hideUntrustedNotes && !isUserTrusted(event.pubkey)) return false
if (filterMutedNotes && mutePubkeys.includes(event.pubkey)) return false
@@ -87,7 +91,7 @@ const NoteList = forwardRef(
idSet.add(id)
return true
})
- }, [newEvents, hideReplies, hideUntrustedNotes, filterMutedNotes, mutePubkeys])
+ }, [newEvents, hideReplies, hideUntrustedNotes, filterMutedNotes, mutePubkeys, isEventDeleted])
const scrollToTop = (behavior: ScrollBehavior = 'instant') => {
setTimeout(() => {
diff --git a/src/components/NoteOptions/useMenuActions.tsx b/src/components/NoteOptions/useMenuActions.tsx
index f573b4c..97f3452 100644
--- a/src/components/NoteOptions/useMenuActions.tsx
+++ b/src/components/NoteOptions/useMenuActions.tsx
@@ -6,7 +6,7 @@ import { useFavoriteRelays } from '@/providers/FavoriteRelaysProvider'
import { useMuteList } from '@/providers/MuteListProvider'
import { useNostr } from '@/providers/NostrProvider'
import client from '@/services/client.service'
-import { Bell, BellOff, Code, Copy, Link, Mail, SatelliteDish, Server } from 'lucide-react'
+import { Bell, BellOff, Code, Copy, Link, Mail, SatelliteDish, Server, Trash2 } from 'lucide-react'
import { Event } from 'nostr-tools'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
@@ -45,7 +45,7 @@ export function useMenuActions({
isSmallScreen
}: UseMenuActionsProps) {
const { t } = useTranslation()
- const { pubkey, relayList } = useNostr()
+ const { pubkey, relayList, attemptDelete } = useNostr()
const { relaySets, favoriteRelays } = useFavoriteRelays()
const { mutePubkeyPublicly, mutePubkeyPrivately, unmutePubkey, mutePubkeys } = useMuteList()
const isMuted = useMemo(() => mutePubkeys.includes(event.pubkey), [mutePubkeys, event])
@@ -235,6 +235,19 @@ export function useMenuActions({
}
}
+ if (pubkey && event.pubkey === pubkey) {
+ actions.push({
+ icon: Trash2,
+ label: t('Try deleting this note'),
+ onClick: () => {
+ closeDrawer()
+ attemptDelete(event)
+ },
+ className: 'text-destructive focus:text-destructive',
+ separator: true
+ })
+ }
+
return actions
}, [
t,
diff --git a/src/i18n/locales/ar.ts b/src/i18n/locales/ar.ts
index e6c7350..acd22f4 100644
--- a/src/i18n/locales/ar.ts
+++ b/src/i18n/locales/ar.ts
@@ -367,6 +367,8 @@ export default {
'Remember my choice': 'تذكر اختياري',
Apply: 'تطبيق',
Reset: 'إعادة تعيين',
- 'Share something on this Relay': 'شارك شيئاً على هذا الريلاي'
+ 'Share something on this Relay': 'شارك شيئاً على هذا الريلاي',
+ 'Try deleting this note': 'حاول حذف هذه الملاحظة',
+ 'Deletion request sent to {{count}} relays': 'تم إرسال طلب الحذف إلى {{count}} ريلايات'
}
}
diff --git a/src/i18n/locales/de.ts b/src/i18n/locales/de.ts
index 80522f7..5c50635 100644
--- a/src/i18n/locales/de.ts
+++ b/src/i18n/locales/de.ts
@@ -375,6 +375,8 @@ export default {
'Remember my choice': 'Meine Auswahl merken',
Apply: 'Anwenden',
Reset: 'Zurücksetzen',
- 'Share something on this Relay': 'Teile etwas auf diesem Relay'
+ 'Share something on this Relay': 'Teile etwas auf diesem Relay',
+ 'Try deleting this note': 'Versuche, diese Notiz zu löschen',
+ 'Deletion request sent to {{count}} relays': 'Löschanfrage an {{count}} Relays gesendet'
}
}
diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts
index 387563b..8a078b0 100644
--- a/src/i18n/locales/en.ts
+++ b/src/i18n/locales/en.ts
@@ -366,6 +366,8 @@ export default {
'Remember my choice': 'Remember my choice',
Apply: 'Apply',
Reset: 'Reset',
- 'Share something on this Relay': 'Share something on this Relay'
+ 'Share something on this Relay': 'Share something on this Relay',
+ 'Try deleting this note': 'Try deleting this note',
+ 'Deletion request sent to {{count}} relays': 'Deletion request sent to {{count}} relays'
}
}
diff --git a/src/i18n/locales/es.ts b/src/i18n/locales/es.ts
index 1750e30..fe04447 100644
--- a/src/i18n/locales/es.ts
+++ b/src/i18n/locales/es.ts
@@ -371,6 +371,9 @@ export default {
'Remember my choice': 'Recordar mi elección',
Apply: 'Aplicar',
Reset: 'Restablecer',
- 'Share something on this Relay': 'Comparte algo en este relé'
+ 'Share something on this Relay': 'Comparte algo en este relé',
+ 'Try deleting this note': 'Intenta eliminar esta nota',
+ 'Deletion request sent to {{count}} relays':
+ 'Solicitud de eliminación enviada a {{count}} relés'
}
}
diff --git a/src/i18n/locales/fa.ts b/src/i18n/locales/fa.ts
index d730f1a..1ae274e 100644
--- a/src/i18n/locales/fa.ts
+++ b/src/i18n/locales/fa.ts
@@ -368,6 +368,8 @@ export default {
'Remember my choice': 'انتخاب من را به خاطر بسپار',
Apply: 'اعمال',
Reset: 'بازنشانی',
- 'Share something on this Relay': 'در این رله چیزی به اشتراک بگذارید'
+ 'Share something on this Relay': 'در این رله چیزی به اشتراک بگذارید',
+ 'Try deleting this note': 'سعی کنید این یادداشت را حذف کنید',
+ 'Deletion request sent to {{count}} relays': 'درخواست حذف به {{count}} رله ارسال شد'
}
}
diff --git a/src/i18n/locales/fr.ts b/src/i18n/locales/fr.ts
index 5728c43..2cf28b6 100644
--- a/src/i18n/locales/fr.ts
+++ b/src/i18n/locales/fr.ts
@@ -373,6 +373,8 @@ export default {
'Remember my choice': 'Se souvenir de mon choix',
Apply: 'Appliquer',
Reset: 'Réinitialiser',
- 'Share something on this Relay': 'Partager quelque chose sur ce relais'
+ 'Share something on this Relay': 'Partager quelque chose sur ce relais',
+ 'Try deleting this note': 'Essayez de supprimer cette note',
+ 'Deletion request sent to {{count}} relays': 'Demande de suppression envoyée à {{count}} relais'
}
}
diff --git a/src/i18n/locales/it.ts b/src/i18n/locales/it.ts
index 9f645b2..cc1b6f7 100644
--- a/src/i18n/locales/it.ts
+++ b/src/i18n/locales/it.ts
@@ -371,6 +371,9 @@ export default {
'Remember my choice': 'Ricorda la mia scelta',
Apply: 'Applica',
Reset: 'Reimposta',
- 'Share something on this Relay': 'Condividi qualcosa su questo Relay'
+ 'Share something on this Relay': 'Condividi qualcosa su questo Relay',
+ 'Try deleting this note': 'Prova a eliminare questa nota',
+ 'Deletion request sent to {{count}} relays':
+ 'Richiesta di eliminazione inviata a {{count}} relays'
}
}
diff --git a/src/i18n/locales/ja.ts b/src/i18n/locales/ja.ts
index 5e4de18..884bed8 100644
--- a/src/i18n/locales/ja.ts
+++ b/src/i18n/locales/ja.ts
@@ -368,6 +368,9 @@ export default {
'Remember my choice': '選択を記憶',
Apply: '適用',
Reset: 'リセット',
- 'Share something on this Relay': 'このリレーで何かを共有する'
+ 'Share something on this Relay': 'このリレーで何かを共有する',
+ 'Try deleting this note': 'このノートを削除してみてください',
+ 'Deletion request sent to {{count}} relays':
+ '削除リクエストが{{count}}個のリレーに送信されました'
}
}
diff --git a/src/i18n/locales/ko.ts b/src/i18n/locales/ko.ts
index 5684323..3787590 100644
--- a/src/i18n/locales/ko.ts
+++ b/src/i18n/locales/ko.ts
@@ -368,6 +368,8 @@ export default {
'Remember my choice': '내 선택 기억하기',
Apply: '적용',
Reset: '초기화',
- 'Share something on this Relay': '이 릴레이에서 무언가를 공유하세요'
+ 'Share something on this Relay': '이 릴레이에서 무언가를 공유하세요',
+ 'Try deleting this note': '이 노트를 삭제해 보세요',
+ 'Deletion request sent to {{count}} relays': '삭제 요청이 {{count}}개의 릴레이로 전송되었습니다'
}
}
diff --git a/src/i18n/locales/pl.ts b/src/i18n/locales/pl.ts
index f06ce12..82835d1 100644
--- a/src/i18n/locales/pl.ts
+++ b/src/i18n/locales/pl.ts
@@ -372,6 +372,9 @@ export default {
'Remember my choice': 'Zapamiętaj mój wybór',
Apply: 'Zastosuj',
Reset: 'Resetuj',
- 'Share something on this Relay': 'Udostępnij coś na tym przekaźniku'
+ 'Share something on this Relay': 'Udostępnij coś na tym przekaźniku',
+ 'Try deleting this note': 'Spróbuj usunąć ten wpis',
+ 'Deletion request sent to {{count}} relays':
+ 'Żądanie usunięcia wysłane do {{count}} przekaźników'
}
}
diff --git a/src/i18n/locales/pt-BR.ts b/src/i18n/locales/pt-BR.ts
index 90390a6..2dcec61 100644
--- a/src/i18n/locales/pt-BR.ts
+++ b/src/i18n/locales/pt-BR.ts
@@ -369,6 +369,8 @@ export default {
'Remember my choice': 'Lembrar minha escolha',
Apply: 'Aplicar',
Reset: 'Redefinir',
- 'Share something on this Relay': 'Compartilhe algo neste Relay'
+ 'Share something on this Relay': 'Compartilhe algo neste Relay',
+ 'Try deleting this note': 'Tente excluir esta nota',
+ 'Deletion request sent to {{count}} relays': 'Pedido de exclusão enviado para {{count}} relays'
}
}
diff --git a/src/i18n/locales/pt-PT.ts b/src/i18n/locales/pt-PT.ts
index 02c101f..c329215 100644
--- a/src/i18n/locales/pt-PT.ts
+++ b/src/i18n/locales/pt-PT.ts
@@ -371,6 +371,9 @@ export default {
'Remember my choice': 'Lembrar a minha escolha',
Apply: 'Aplicar',
Reset: 'Repor',
- 'Share something on this Relay': 'Partilhe algo neste Relay'
+ 'Share something on this Relay': 'Partilhe algo neste Relay',
+ 'Try deleting this note': 'Tente eliminar esta nota',
+ 'Deletion request sent to {{count}} relays':
+ 'Pedido de eliminação enviado para {{count}} relays'
}
}
diff --git a/src/i18n/locales/ru.ts b/src/i18n/locales/ru.ts
index f3314e8..e14bdc4 100644
--- a/src/i18n/locales/ru.ts
+++ b/src/i18n/locales/ru.ts
@@ -372,6 +372,8 @@ export default {
'Remember my choice': 'Запомнить мой выбор',
Apply: 'Применить',
Reset: 'Сбросить',
- 'Share something on this Relay': 'Поделиться чем-то на этом релее'
+ 'Share something on this Relay': 'Поделиться чем-то на этом релее',
+ 'Try deleting this note': 'Попробуйте удалить эту заметку',
+ 'Deletion request sent to {{count}} relays': 'Запрос на удаление отправлен на {{count}} релеев'
}
}
diff --git a/src/i18n/locales/th.ts b/src/i18n/locales/th.ts
index 17c7e09..1ec9aca 100644
--- a/src/i18n/locales/th.ts
+++ b/src/i18n/locales/th.ts
@@ -365,6 +365,8 @@ export default {
'Remember my choice': 'จำการเลือกของฉัน',
Apply: 'ใช้',
Reset: 'รีเซ็ต',
- 'Share something on this Relay': 'แชร์บางอย่างบนรีเลย์นี้'
+ 'Share something on this Relay': 'แชร์บางอย่างบนรีเลย์นี้',
+ 'Try deleting this note': 'ลองลบโน้ตนี้ดู',
+ 'Deletion request sent to {{count}} relays': 'คำขอลบถูกส่งไปยังรีเลย์ {{count}} รายการ'
}
}
diff --git a/src/i18n/locales/zh.ts b/src/i18n/locales/zh.ts
index b3ba4fd..5af5760 100644
--- a/src/i18n/locales/zh.ts
+++ b/src/i18n/locales/zh.ts
@@ -363,6 +363,8 @@ export default {
'Remember my choice': '记住我的选择',
Apply: '应用',
Reset: '重置',
- 'Share something on this Relay': '在此服务器上分享点什么'
+ 'Share something on this Relay': '在此服务器上分享点什么',
+ 'Try deleting this note': '尝试删除此笔记',
+ 'Deletion request sent to {{count}} relays': '删除请求已发送到 {{count}} 个服务器'
}
}
diff --git a/src/lib/draft-event.ts b/src/lib/draft-event.ts
index e4b02d6..7506a63 100644
--- a/src/lib/draft-event.ts
+++ b/src/lib/draft-event.ts
@@ -416,6 +416,22 @@ export function createPollResponseDraftEvent(
}
}
+export function createDeletionRequestDraftEvent(event: Event): TDraftEvent {
+ const tags: string[][] = [buildKTag(event.kind)]
+ if (isReplaceableEvent(event.kind)) {
+ tags.push(['a', getReplaceableCoordinateFromEvent(event)])
+ } else {
+ tags.push(['e', event.id])
+ }
+
+ return {
+ kind: kinds.EventDeletion,
+ content: 'Request for deletion of the event.',
+ tags,
+ created_at: dayjs().unix()
+ }
+}
+
function generateImetaTags(imageUrls: string[]) {
return imageUrls
.map((imageUrl) => {
diff --git a/src/providers/DeletedEventProvider.tsx b/src/providers/DeletedEventProvider.tsx
new file mode 100644
index 0000000..a4ac026
--- /dev/null
+++ b/src/providers/DeletedEventProvider.tsx
@@ -0,0 +1,43 @@
+import { getReplaceableCoordinateFromEvent, isReplaceableEvent } from '@/lib/event'
+import { NostrEvent } from 'nostr-tools'
+import { createContext, useCallback, useContext, useState } from 'react'
+
+type TDeletedEventContext = {
+ addDeletedEvent: (event: NostrEvent) => void
+ isEventDeleted: (event: NostrEvent) => boolean
+}
+
+const DeletedEventContext = createContext(undefined)
+
+export const useDeletedEvent = () => {
+ const context = useContext(DeletedEventContext)
+ if (!context) {
+ throw new Error('useDeletedEvent must be used within a DeletedEventProvider')
+ }
+ return context
+}
+
+export function DeletedEventProvider({ children }: { children: React.ReactNode }) {
+ const [deletedEventKeys, setDeletedEventKeys] = useState>(new Set())
+
+ const isEventDeleted = useCallback(
+ (event: NostrEvent) => {
+ return deletedEventKeys.has(getKey(event))
+ },
+ [deletedEventKeys]
+ )
+
+ const addDeletedEvent = (event: NostrEvent) => {
+ setDeletedEventKeys((prev) => new Set(prev).add(getKey(event)))
+ }
+
+ return (
+
+ {children}
+
+ )
+}
+
+function getKey(event: NostrEvent) {
+ return isReplaceableEvent(event.kind) ? getReplaceableCoordinateFromEvent(event) : event.id
+}
diff --git a/src/providers/NostrProvider/index.tsx b/src/providers/NostrProvider/index.tsx
index d263768..8a567c1 100644
--- a/src/providers/NostrProvider/index.tsx
+++ b/src/providers/NostrProvider/index.tsx
@@ -1,12 +1,13 @@
import LoginDialog from '@/components/LoginDialog'
import { ApplicationDataKey, BIG_RELAY_URLS, ExtendedKind } from '@/constants'
import {
+ createDeletionRequestDraftEvent,
createFollowListDraftEvent,
createMuteListDraftEvent,
createRelayListDraftEvent,
createSeenNotificationsAtDraftEvent
} from '@/lib/draft-event'
-import { getLatestEvent, getReplaceableEventIdentifier } from '@/lib/event'
+import { getLatestEvent, getReplaceableEventIdentifier, isProtectedEvent } from '@/lib/event'
import { getProfileFromEvent, getRelayListFromEvent } from '@/lib/event-metadata'
import { formatPubkey, isValidPubkey, pubkeyToNpub } from '@/lib/pubkey'
import client from '@/services/client.service'
@@ -28,6 +29,7 @@ import { Nip07Signer } from './nip-07.signer'
import { NostrConnectionSigner } from './nostrConnection.signer'
import { NpubSigner } from './npub.signer'
import { NsecSigner } from './nsec.signer'
+import { useDeletedEvent } from '../DeletedEventProvider'
type TPublishOptions = {
specifiedRelayUrls?: string[]
@@ -62,6 +64,7 @@ type TNostrContext = {
* Default publish the event to current relays, user's write relays and additional relays
*/
publish: (draftEvent: TDraftEvent, options?: TPublishOptions) => Promise
+ attemptDelete: (targetEvent: Event) => Promise
signHttpAuth: (url: string, method: string) => Promise
signEvent: (draftEvent: TDraftEvent) => Promise
nip04Encrypt: (pubkey: string, plainText: string) => Promise
@@ -91,6 +94,7 @@ export const useNostr = () => {
export function NostrProvider({ children }: { children: React.ReactNode }) {
const { t } = useTranslation()
+ const { addDeletedEvent } = useDeletedEvent()
const [accounts, setAccounts] = useState(
storage.getAccounts().map((act) => ({ pubkey: act.pubkey, signerType: act.signerType }))
)
@@ -587,10 +591,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
return event as VerifiedEvent
}
- const publish = async (
- draftEvent: TDraftEvent,
- { specifiedRelayUrls, additionalRelayUrls }: TPublishOptions = {}
- ) => {
+ const publish = async (draftEvent: TDraftEvent, options: TPublishOptions = {}) => {
if (!account || !signer || account.signerType === 'npub') {
throw new Error('You need to login first')
}
@@ -610,56 +611,32 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
}
}
- const _additionalRelayUrls: string[] = additionalRelayUrls ?? []
- if (
- !specifiedRelayUrls?.length &&
- ![kinds.Contacts, kinds.Mutelist].includes(draftEvent.kind)
- ) {
- const mentions: string[] = []
- draftEvent.tags.forEach(([tagName, tagValue]) => {
- if (
- ['p', 'P'].includes(tagName) &&
- !!tagValue &&
- isValidPubkey(tagValue) &&
- !mentions.includes(tagValue)
- ) {
- mentions.push(tagValue)
- }
- })
- if (mentions.length > 0) {
- const relayLists = await client.fetchRelayLists(mentions)
- relayLists.forEach((relayList) => {
- _additionalRelayUrls.push(...relayList.read.slice(0, 4))
- })
- }
+ const relays = await determineTargetRelays(event, options)
+
+ await client.publishEvent(relays, event)
+ return event
+ }
+
+ const attemptDelete = async (targetEvent: Event) => {
+ if (!signer) {
+ throw new Error(t('You need to login first'))
}
- if (
- [
- kinds.RelayList,
- kinds.Contacts,
- ExtendedKind.FAVORITE_RELAYS,
- ExtendedKind.BLOSSOM_SERVER_LIST
- ].includes(draftEvent.kind)
- ) {
- _additionalRelayUrls.push(...BIG_RELAY_URLS)
+ if (account?.pubkey !== targetEvent.pubkey) {
+ throw new Error(t('You can only delete your own notes'))
}
- let relays: string[]
- if (specifiedRelayUrls?.length) {
- relays = specifiedRelayUrls
- } else {
- const relayList = await client.fetchRelayList(event.pubkey)
- relays = (relayList?.write.slice(0, 10) ?? []).concat(
- Array.from(new Set(_additionalRelayUrls)) ?? []
- )
- }
+ const deletionRequest = await signEvent(createDeletionRequestDraftEvent(targetEvent))
- if (!relays.length) {
- relays.push(...BIG_RELAY_URLS)
- }
+ const seenOn = client.getSeenEventRelayUrls(targetEvent.id)
+ const relays = await determineTargetRelays(targetEvent, {
+ specifiedRelayUrls: isProtectedEvent(targetEvent) ? seenOn : undefined,
+ additionalRelayUrls: seenOn
+ })
- await client.publishEvent(relays, event)
- return event
+ await client.publishEvent(relays, deletionRequest)
+
+ addDeletedEvent(targetEvent)
+ toast.success(t('Deletion request sent to {{count}} relays', { count: relays.length }))
}
const signHttpAuth = async (url: string, method: string, content = '') => {
@@ -779,6 +756,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
npubLogin,
removeAccount,
publish,
+ attemptDelete,
signHttpAuth,
nip04Encrypt,
nip04Decrypt,
@@ -799,3 +777,55 @@ export function NostrProvider({ children }: { children: React.ReactNode }) {
)
}
+
+async function determineTargetRelays(
+ event: Event,
+ { specifiedRelayUrls, additionalRelayUrls }: TPublishOptions = {}
+) {
+ const _additionalRelayUrls: string[] = additionalRelayUrls ?? []
+ if (!specifiedRelayUrls?.length && ![kinds.Contacts, kinds.Mutelist].includes(event.kind)) {
+ const mentions: string[] = []
+ event.tags.forEach(([tagName, tagValue]) => {
+ if (
+ ['p', 'P'].includes(tagName) &&
+ !!tagValue &&
+ isValidPubkey(tagValue) &&
+ !mentions.includes(tagValue)
+ ) {
+ mentions.push(tagValue)
+ }
+ })
+ if (mentions.length > 0) {
+ const relayLists = await client.fetchRelayLists(mentions)
+ relayLists.forEach((relayList) => {
+ _additionalRelayUrls.push(...relayList.read.slice(0, 4))
+ })
+ }
+ }
+ if (
+ [
+ kinds.RelayList,
+ kinds.Contacts,
+ ExtendedKind.FAVORITE_RELAYS,
+ ExtendedKind.BLOSSOM_SERVER_LIST
+ ].includes(event.kind)
+ ) {
+ _additionalRelayUrls.push(...BIG_RELAY_URLS)
+ }
+
+ let relays: string[]
+ if (specifiedRelayUrls?.length) {
+ relays = specifiedRelayUrls
+ } else {
+ const relayList = await client.fetchRelayList(event.pubkey)
+ relays = (relayList?.write.slice(0, 10) ?? []).concat(
+ Array.from(new Set(_additionalRelayUrls)) ?? []
+ )
+ }
+
+ if (!relays.length) {
+ relays.push(...BIG_RELAY_URLS)
+ }
+
+ return relays
+}