From b3b7176bcdcd9a706c6914716144f94792916e36 Mon Sep 17 00:00:00 2001 From: codytseng Date: Sun, 31 Aug 2025 00:08:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=92=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/NoteOptions/useMenuActions.tsx | 8 +- src/i18n/locales/ar.ts | 3 +- src/i18n/locales/de.ts | 3 +- src/i18n/locales/en.ts | 3 +- src/i18n/locales/es.ts | 3 +- src/i18n/locales/fa.ts | 3 +- src/i18n/locales/fr.ts | 4 +- src/i18n/locales/it.ts | 3 +- src/i18n/locales/ja.ts | 3 +- src/i18n/locales/ko.ts | 4 +- src/i18n/locales/pl.ts | 3 +- src/i18n/locales/pt-BR.ts | 3 +- src/i18n/locales/pt-PT.ts | 3 +- src/i18n/locales/ru.ts | 3 +- src/i18n/locales/th.ts | 3 +- src/i18n/locales/zh.ts | 3 +- src/providers/NostrProvider/index.tsx | 75 ++++--------------- src/services/client.service.ts | 56 +++++++++++++- src/types/index.d.ts | 5 ++ 19 files changed, 108 insertions(+), 83 deletions(-) diff --git a/src/components/NoteOptions/useMenuActions.tsx b/src/components/NoteOptions/useMenuActions.tsx index 97f3452..a3ef994 100644 --- a/src/components/NoteOptions/useMenuActions.tsx +++ b/src/components/NoteOptions/useMenuActions.tsx @@ -45,24 +45,24 @@ export function useMenuActions({ isSmallScreen }: UseMenuActionsProps) { const { t } = useTranslation() - const { pubkey, relayList, attemptDelete } = useNostr() + const { pubkey, attemptDelete } = useNostr() const { relaySets, favoriteRelays } = useFavoriteRelays() const { mutePubkeyPublicly, mutePubkeyPrivately, unmutePubkey, mutePubkeys } = useMuteList() const isMuted = useMemo(() => mutePubkeys.includes(event.pubkey), [mutePubkeys, event]) const broadcastSubMenu: SubMenuAction[] = useMemo(() => { const items = [] - if (pubkey) { + if (pubkey && event.pubkey === pubkey) { items.push({ label: (
-
{t('Write relays')}
+
{t('Suitable Relays')}
), onClick: async () => { closeDrawer() - const relays = relayList?.write.slice(0, 10) + const relays = await client.determineTargetRelays(event) if (relays?.length) { await client .publishEvent(relays, event) diff --git a/src/i18n/locales/ar.ts b/src/i18n/locales/ar.ts index acd22f4..8ede9ac 100644 --- a/src/i18n/locales/ar.ts +++ b/src/i18n/locales/ar.ts @@ -369,6 +369,7 @@ export default { Reset: 'إعادة تعيين', 'Share something on this Relay': 'شارك شيئاً على هذا الريلاي', 'Try deleting this note': 'حاول حذف هذه الملاحظة', - 'Deletion request sent to {{count}} relays': 'تم إرسال طلب الحذف إلى {{count}} ريلايات' + 'Deletion request sent to {{count}} relays': 'تم إرسال طلب الحذف إلى {{count}} ريلايات', + 'Suitable Relays': 'الريلايات المناسبة' } } diff --git a/src/i18n/locales/de.ts b/src/i18n/locales/de.ts index 5c50635..f46f762 100644 --- a/src/i18n/locales/de.ts +++ b/src/i18n/locales/de.ts @@ -377,6 +377,7 @@ export default { Reset: 'Zurücksetzen', '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' + 'Deletion request sent to {{count}} relays': 'Löschanfrage an {{count}} Relays gesendet', + 'Suitable Relays': 'Geeignete Relays' } } diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index 8a078b0..e106e8d 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -368,6 +368,7 @@ export default { Reset: 'Reset', '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' + 'Deletion request sent to {{count}} relays': 'Deletion request sent to {{count}} relays', + 'Suitable Relays': 'Suitable Relays' } } diff --git a/src/i18n/locales/es.ts b/src/i18n/locales/es.ts index fe04447..d880d7b 100644 --- a/src/i18n/locales/es.ts +++ b/src/i18n/locales/es.ts @@ -374,6 +374,7 @@ export default { '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' + 'Solicitud de eliminación enviada a {{count}} relés', + 'Suitable Relays': 'Relés adecuados' } } diff --git a/src/i18n/locales/fa.ts b/src/i18n/locales/fa.ts index 1ae274e..1b78605 100644 --- a/src/i18n/locales/fa.ts +++ b/src/i18n/locales/fa.ts @@ -370,6 +370,7 @@ export default { Reset: 'بازنشانی', 'Share something on this Relay': 'در این رله چیزی به اشتراک بگذارید', 'Try deleting this note': 'سعی کنید این یادداشت را حذف کنید', - 'Deletion request sent to {{count}} relays': 'درخواست حذف به {{count}} رله ارسال شد' + 'Deletion request sent to {{count}} relays': 'درخواست حذف به {{count}} رله ارسال شد', + 'Suitable Relays': 'رله‌های مناسب' } } diff --git a/src/i18n/locales/fr.ts b/src/i18n/locales/fr.ts index 2cf28b6..eb31360 100644 --- a/src/i18n/locales/fr.ts +++ b/src/i18n/locales/fr.ts @@ -375,6 +375,8 @@ export default { Reset: 'Réinitialiser', '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' + 'Deletion request sent to {{count}} relays': + 'Demande de suppression envoyée à {{count}} relais', + 'Suitable Relays': 'Relais adaptés' } } diff --git a/src/i18n/locales/it.ts b/src/i18n/locales/it.ts index cc1b6f7..0e8bbb4 100644 --- a/src/i18n/locales/it.ts +++ b/src/i18n/locales/it.ts @@ -374,6 +374,7 @@ export default { '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' + 'Richiesta di eliminazione inviata a {{count}} relays', + 'Suitable Relays': 'Relays adatti' } } diff --git a/src/i18n/locales/ja.ts b/src/i18n/locales/ja.ts index 884bed8..76c3e98 100644 --- a/src/i18n/locales/ja.ts +++ b/src/i18n/locales/ja.ts @@ -371,6 +371,7 @@ export default { 'Share something on this Relay': 'このリレーで何かを共有する', 'Try deleting this note': 'このノートを削除してみてください', 'Deletion request sent to {{count}} relays': - '削除リクエストが{{count}}個のリレーに送信されました' + '削除リクエストが{{count}}個のリレーに送信されました', + 'Suitable Relays': '適切なリレー' } } diff --git a/src/i18n/locales/ko.ts b/src/i18n/locales/ko.ts index 3787590..4fc481f 100644 --- a/src/i18n/locales/ko.ts +++ b/src/i18n/locales/ko.ts @@ -370,6 +370,8 @@ export default { Reset: '초기화', 'Share something on this Relay': '이 릴레이에서 무언가를 공유하세요', 'Try deleting this note': '이 노트를 삭제해 보세요', - 'Deletion request sent to {{count}} relays': '삭제 요청이 {{count}}개의 릴레이로 전송되었습니다' + 'Deletion request sent to {{count}} relays': + '삭제 요청이 {{count}}개의 릴레이로 전송되었습니다', + 'Suitable Relays': '적합한 릴레이' } } diff --git a/src/i18n/locales/pl.ts b/src/i18n/locales/pl.ts index 82835d1..c7e8aeb 100644 --- a/src/i18n/locales/pl.ts +++ b/src/i18n/locales/pl.ts @@ -375,6 +375,7 @@ export default { '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' + 'Żądanie usunięcia wysłane do {{count}} przekaźników', + 'Suitable Relays': 'Odpowiednie przekaźniki' } } diff --git a/src/i18n/locales/pt-BR.ts b/src/i18n/locales/pt-BR.ts index e981026..656c0a3 100644 --- a/src/i18n/locales/pt-BR.ts +++ b/src/i18n/locales/pt-BR.ts @@ -371,6 +371,7 @@ export default { Reset: 'Redefinir', 'Share something on this Relay': 'Compartilhe algo neste Relay', 'Try deleting this note': 'Solicitar exclusão desta nota', - 'Deletion request sent to {{count}} relays': 'Pedido de exclusão enviado para {{count}} relays' + 'Deletion request sent to {{count}} relays': 'Pedido de exclusão enviado para {{count}} relays', + 'Suitable Relays': 'Relays adequados' } } diff --git a/src/i18n/locales/pt-PT.ts b/src/i18n/locales/pt-PT.ts index c329215..a4ed3a8 100644 --- a/src/i18n/locales/pt-PT.ts +++ b/src/i18n/locales/pt-PT.ts @@ -374,6 +374,7 @@ export default { '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' + 'Pedido de eliminação enviado para {{count}} relays', + 'Suitable Relays': 'Relays adequados' } } diff --git a/src/i18n/locales/ru.ts b/src/i18n/locales/ru.ts index e14bdc4..3eeace5 100644 --- a/src/i18n/locales/ru.ts +++ b/src/i18n/locales/ru.ts @@ -374,6 +374,7 @@ export default { Reset: 'Сбросить', 'Share something on this Relay': 'Поделиться чем-то на этом релее', 'Try deleting this note': 'Попробуйте удалить эту заметку', - 'Deletion request sent to {{count}} relays': 'Запрос на удаление отправлен на {{count}} релеев' + 'Deletion request sent to {{count}} relays': 'Запрос на удаление отправлен на {{count}} релеев', + 'Suitable Relays': 'Подходящие релея' } } diff --git a/src/i18n/locales/th.ts b/src/i18n/locales/th.ts index 1ec9aca..52c92c1 100644 --- a/src/i18n/locales/th.ts +++ b/src/i18n/locales/th.ts @@ -367,6 +367,7 @@ export default { Reset: 'รีเซ็ต', 'Share something on this Relay': 'แชร์บางอย่างบนรีเลย์นี้', 'Try deleting this note': 'ลองลบโน้ตนี้ดู', - 'Deletion request sent to {{count}} relays': 'คำขอลบถูกส่งไปยังรีเลย์ {{count}} รายการ' + 'Deletion request sent to {{count}} relays': 'คำขอลบถูกส่งไปยังรีเลย์ {{count}} รายการ', + 'Suitable Relays': 'รีเลย์ที่เหมาะสม' } } diff --git a/src/i18n/locales/zh.ts b/src/i18n/locales/zh.ts index 5af5760..7094345 100644 --- a/src/i18n/locales/zh.ts +++ b/src/i18n/locales/zh.ts @@ -365,6 +365,7 @@ export default { Reset: '重置', 'Share something on this Relay': '在此服务器上分享点什么', 'Try deleting this note': '尝试删除此笔记', - 'Deletion request sent to {{count}} relays': '删除请求已发送到 {{count}} 个服务器' + 'Deletion request sent to {{count}} relays': '删除请求已发送到 {{count}} 个服务器', + 'Suitable Relays': '适合的服务器' } } diff --git a/src/providers/NostrProvider/index.tsx b/src/providers/NostrProvider/index.tsx index 8a567c1..9cffc10 100644 --- a/src/providers/NostrProvider/index.tsx +++ b/src/providers/NostrProvider/index.tsx @@ -9,13 +9,21 @@ import { } from '@/lib/draft-event' import { getLatestEvent, getReplaceableEventIdentifier, isProtectedEvent } from '@/lib/event' import { getProfileFromEvent, getRelayListFromEvent } from '@/lib/event-metadata' -import { formatPubkey, isValidPubkey, pubkeyToNpub } from '@/lib/pubkey' +import { formatPubkey, pubkeyToNpub } from '@/lib/pubkey' import client from '@/services/client.service' import customEmojiService from '@/services/custom-emoji.service' import indexedDb from '@/services/indexed-db.service' import storage from '@/services/local-storage.service' import noteStatsService from '@/services/note-stats.service' -import { ISigner, TAccount, TAccountPointer, TDraftEvent, TProfile, TRelayList } from '@/types' +import { + ISigner, + TAccount, + TAccountPointer, + TDraftEvent, + TProfile, + TPublishOptions, + TRelayList +} from '@/types' import { hexToBytes } from '@noble/hashes/utils' import dayjs from 'dayjs' import { Event, kinds, VerifiedEvent } from 'nostr-tools' @@ -24,17 +32,12 @@ import * as nip49 from 'nostr-tools/nip49' import { createContext, useContext, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { toast } from 'sonner' +import { useDeletedEvent } from '../DeletedEventProvider' import { BunkerSigner } from './bunker.signer' 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[] - additionalRelayUrls?: string[] -} type TNostrContext = { isInitialized: boolean @@ -611,7 +614,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { } } - const relays = await determineTargetRelays(event, options) + const relays = await client.determineTargetRelays(event, options) await client.publishEvent(relays, event) return event @@ -628,7 +631,7 @@ export function NostrProvider({ children }: { children: React.ReactNode }) { const deletionRequest = await signEvent(createDeletionRequestDraftEvent(targetEvent)) const seenOn = client.getSeenEventRelayUrls(targetEvent.id) - const relays = await determineTargetRelays(targetEvent, { + const relays = await client.determineTargetRelays(targetEvent, { specifiedRelayUrls: isProtectedEvent(targetEvent) ? seenOn : undefined, additionalRelayUrls: seenOn }) @@ -777,55 +780,3 @@ 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 -} diff --git a/src/services/client.service.ts b/src/services/client.service.ts index 8b1f69a..0909e41 100644 --- a/src/services/client.service.ts +++ b/src/services/client.service.ts @@ -6,11 +6,11 @@ import { isReplaceableEvent } from '@/lib/event' import { getProfileFromEvent, getRelayListFromEvent } from '@/lib/event-metadata' -import { formatPubkey, pubkeyToNpub, userIdToPubkey } from '@/lib/pubkey' +import { formatPubkey, isValidPubkey, pubkeyToNpub, userIdToPubkey } from '@/lib/pubkey' import { getPubkeysFromPTags, getServersFromServerTags } from '@/lib/tag' import { isLocalNetworkUrl, isWebsocketUrl, normalizeUrl } from '@/lib/url' import { isSafari } from '@/lib/utils' -import { ISigner, TProfile, TRelayList, TSubRequestFilter } from '@/types' +import { ISigner, TProfile, TPublishOptions, TRelayList, TSubRequestFilter } from '@/types' import { sha256 } from '@noble/hashes/sha2' import DataLoader from 'dataloader' import dayjs from 'dayjs' @@ -80,6 +80,58 @@ class ClientService extends EventTarget { await indexedDb.iterateProfileEvents((profileEvent) => this.addUsernameToIndex(profileEvent)) } + async determineTargetRelays( + event: NEvent, + { 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 this.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 this.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 + } + async publishEvent(relayUrls: string[], event: NEvent) { try { const uniqueRelayUrls = Array.from(new Set(relayUrls)) diff --git a/src/types/index.d.ts b/src/types/index.d.ts index 2128297..3fc5d38 100644 --- a/src/types/index.d.ts +++ b/src/types/index.d.ts @@ -116,6 +116,11 @@ export type TImetaInfo = { pubkey?: string } +export type TPublishOptions = { + specifiedRelayUrls?: string[] + additionalRelayUrls?: string[] +} + export type TNoteListMode = 'posts' | 'postsAndReplies' | 'you' export type TNotificationType = 'all' | 'mentions' | 'reactions' | 'zaps'