diff --git a/src/components/NoteOptions/DesktopMenu.tsx b/src/components/NoteOptions/DesktopMenu.tsx new file mode 100644 index 00000000..a888aeba --- /dev/null +++ b/src/components/NoteOptions/DesktopMenu.tsx @@ -0,0 +1,62 @@ +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuSeparator, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuTrigger +} from '@/components/ui/dropdown-menu' +import { cn } from '@/lib/utils' +import { MenuAction } from './useMenuActions' + +interface DesktopMenuProps { + menuActions: MenuAction[] + trigger: React.ReactNode +} + +export function DesktopMenu({ menuActions, trigger }: DesktopMenuProps) { + return ( + + {trigger} + + {menuActions.map((action, index) => { + const Icon = action.icon + return ( +
+ {action.separator && index > 0 && } + {action.subMenu ? ( + + + + {action.label} + + + {action.subMenu.map((subAction, subIndex) => ( + <> + {subAction.separator && subIndex > 0 && } + + {subAction.label} + + + ))} + + + ) : ( + + + {action.label} + + )} +
+ ) + })} +
+
+ ) +} diff --git a/src/components/NoteOptions/MobileMenu.tsx b/src/components/NoteOptions/MobileMenu.tsx new file mode 100644 index 00000000..60b1b8c2 --- /dev/null +++ b/src/components/NoteOptions/MobileMenu.tsx @@ -0,0 +1,79 @@ +import { Button } from '@/components/ui/button' +import { Drawer, DrawerContent, DrawerOverlay } from '@/components/ui/drawer' +import { ArrowLeft } from 'lucide-react' +import { MenuAction, SubMenuAction } from './useMenuActions' + +interface MobileMenuProps { + menuActions: MenuAction[] + trigger: React.ReactNode + isDrawerOpen: boolean + setIsDrawerOpen: (open: boolean) => void + showSubMenu: boolean + activeSubMenu: SubMenuAction[] + subMenuTitle: string + closeDrawer: () => void + goBackToMainMenu: () => void +} + +export function MobileMenu({ + menuActions, + trigger, + isDrawerOpen, + setIsDrawerOpen, + showSubMenu, + activeSubMenu, + subMenuTitle, + closeDrawer, + goBackToMainMenu +}: MobileMenuProps) { + return ( + <> + {trigger} + + + +
+ {!showSubMenu ? ( + menuActions.map((action, index) => { + const Icon = action.icon + return ( + + ) + }) + ) : ( + <> + +
+ {activeSubMenu.map((subAction, index) => ( + + ))} + + )} +
+ + + + ) +} diff --git a/src/components/NoteOptions/index.tsx b/src/components/NoteOptions/index.tsx index c4307230..3f5f7ea9 100644 --- a/src/components/NoteOptions/index.tsx +++ b/src/components/NoteOptions/index.tsx @@ -1,32 +1,42 @@ -import { Button } from '@/components/ui/button' -import { Drawer, DrawerContent, DrawerOverlay } from '@/components/ui/drawer' -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuSeparator, - DropdownMenuTrigger -} from '@/components/ui/dropdown-menu' -import { getNoteBech32Id } from '@/lib/event' -import { toNjump } from '@/lib/link' -import { pubkeyToNpub } from '@/lib/pubkey' -import { useMuteList } from '@/providers/MuteListProvider' -import { useNostr } from '@/providers/NostrProvider' import { useScreenSize } from '@/providers/ScreenSizeProvider' -import { Bell, BellOff, Code, Copy, Ellipsis, Link } from 'lucide-react' +import { Ellipsis } from 'lucide-react' import { Event } from 'nostr-tools' -import { useMemo, useState } from 'react' -import { useTranslation } from 'react-i18next' +import { useState } from 'react' +import { DesktopMenu } from './DesktopMenu' +import { MobileMenu } from './MobileMenu' import RawEventDialog from './RawEventDialog' +import { SubMenuAction, useMenuActions } from './useMenuActions' export default function NoteOptions({ event, className }: { event: Event; className?: string }) { - const { t } = useTranslation() const { isSmallScreen } = useScreenSize() - const { pubkey } = useNostr() const [isRawEventDialogOpen, setIsRawEventDialogOpen] = useState(false) const [isDrawerOpen, setIsDrawerOpen] = useState(false) - const { mutePubkeyPublicly, mutePubkeyPrivately, unmutePubkey, mutePubkeys } = useMuteList() - const isMuted = useMemo(() => mutePubkeys.includes(event.pubkey), [mutePubkeys, event]) + const [showSubMenu, setShowSubMenu] = useState(false) + const [activeSubMenu, setActiveSubMenu] = useState([]) + const [subMenuTitle, setSubMenuTitle] = useState('') + + const closeDrawer = () => { + setIsDrawerOpen(false) + setShowSubMenu(false) + } + + const goBackToMainMenu = () => { + setShowSubMenu(false) + } + + const showSubMenuActions = (subMenu: SubMenuAction[], title: string) => { + setActiveSubMenu(subMenu) + setSubMenuTitle(title) + setShowSubMenu(true) + } + + const menuActions = useMenuActions({ + event, + closeDrawer, + showSubMenuActions, + setIsRawEventDialogOpen, + isSmallScreen + }) const trigger = ( - - - - {pubkey && - (isMuted ? ( - - ) : ( - <> - - - - ))} -
-
-
- {rawEventDialog} - - ) - } - return (
e.stopPropagation()}> - - {trigger} - - navigator.clipboard.writeText(getNoteBech32Id(event))}> - - {t('Copy event ID')} - - navigator.clipboard.writeText(pubkeyToNpub(event.pubkey) ?? '')} - > - - {t('Copy user ID')} - - navigator.clipboard.writeText(toNjump(getNoteBech32Id(event)))} - > - - {t('Copy share link')} - + {isSmallScreen ? ( + + ) : ( + + )} - - - setIsRawEventDialogOpen(true)}> - - {t('View raw event')} - - {pubkey && ( - <> - - {isMuted ? ( - unmutePubkey(event.pubkey)} - className="text-destructive focus:text-destructive" - > - - {t('Unmute user')} - - ) : ( - <> - mutePubkeyPrivately(event.pubkey)} - className="text-destructive focus:text-destructive" - > - - {t('Mute user privately')} - - mutePubkeyPublicly(event.pubkey)} - className="text-destructive focus:text-destructive" - > - - {t('Mute user publicly')} - - - )} - - )} - - - {rawEventDialog} + setIsRawEventDialogOpen(false)} + />
) } diff --git a/src/components/NoteOptions/useMenuActions.tsx b/src/components/NoteOptions/useMenuActions.tsx new file mode 100644 index 00000000..7157d18f --- /dev/null +++ b/src/components/NoteOptions/useMenuActions.tsx @@ -0,0 +1,255 @@ +import { getNoteBech32Id, isProtectedEvent } from '@/lib/event' +import { toNjump } from '@/lib/link' +import { pubkeyToNpub } from '@/lib/pubkey' +import { simplifyUrl } from '@/lib/url' +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, Globe, Link, Mail, Server } from 'lucide-react' +import { Event } from 'nostr-tools' +import { useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import { toast } from 'sonner' +import RelayIcon from '../RelayIcon' + +export interface SubMenuAction { + label: React.ReactNode + onClick: () => void + className?: string + separator?: boolean +} + +export interface MenuAction { + icon: React.ComponentType + label: string + onClick?: () => void + className?: string + separator?: boolean + subMenu?: SubMenuAction[] +} + +interface UseMenuActionsProps { + event: Event + closeDrawer: () => void + showSubMenuActions: (subMenu: SubMenuAction[], title: string) => void + setIsRawEventDialogOpen: (open: boolean) => void + isSmallScreen: boolean +} + +export function useMenuActions({ + event, + closeDrawer, + showSubMenuActions, + setIsRawEventDialogOpen, + isSmallScreen +}: UseMenuActionsProps) { + const { t } = useTranslation() + const { pubkey, relayList } = 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) { + items.push({ + label: ( +
+ +
{t('Write relays')}
+
+ ), + onClick: async () => { + closeDrawer() + const relays = relayList?.write.slice(0, 10) + if (relays?.length) { + await client + .publishEvent(relays, event) + .then(() => { + toast.success(t('Successfully broadcasted to your write relays')) + }) + .catch((error) => { + toast.error( + t('Failed to broadcast to your write relays: {{error}}', { error: error.message }) + ) + }) + } + } + }) + } + + if (relaySets.length) { + items.push( + ...relaySets + .filter((set) => set.relayUrls.length) + .map((set, index) => ({ + label: ( +
+ +
{set.name}
+
+ ), + onClick: async () => { + closeDrawer() + await client + .publishEvent(set.relayUrls, event) + .then(() => { + toast.success( + t('Successfully broadcasted to relay set: {{name}}', { name: set.name }) + ) + }) + .catch((error) => { + toast.error( + t('Failed to broadcast to relay set: {{name}}. Error: {{error}}', { + name: set.name, + error: error.message + }) + ) + }) + }, + separator: index === 0 + })) + ) + } + + if (favoriteRelays.length) { + items.push( + ...favoriteRelays.map((relay, index) => ({ + label: ( +
+ +
{simplifyUrl(relay)}
+
+ ), + onClick: async () => { + closeDrawer() + await client + .publishEvent([relay], event) + .then(() => { + toast.success( + t('Successfully broadcasted to relay: {{url}}', { url: simplifyUrl(relay) }) + ) + }) + .catch((error) => { + toast.error( + t('Failed to broadcast to relay: {{url}}. Error: {{error}}', { + url: simplifyUrl(relay), + error: error.message + }) + ) + }) + }, + separator: index === 0 + })) + ) + } + + return items + }, [pubkey, favoriteRelays, relaySets]) + + const menuActions: MenuAction[] = useMemo(() => { + const actions: MenuAction[] = [ + { + icon: Copy, + label: t('Copy event ID'), + onClick: () => { + navigator.clipboard.writeText(getNoteBech32Id(event)) + closeDrawer() + } + }, + { + icon: Copy, + label: t('Copy user ID'), + onClick: () => { + navigator.clipboard.writeText(pubkeyToNpub(event.pubkey) ?? '') + closeDrawer() + } + }, + { + icon: Link, + label: t('Copy share link'), + onClick: () => { + navigator.clipboard.writeText(toNjump(getNoteBech32Id(event))) + closeDrawer() + } + }, + { + icon: Code, + label: t('View raw event'), + onClick: () => { + closeDrawer() + setIsRawEventDialogOpen(true) + }, + separator: true + } + ] + + const isProtected = isProtectedEvent(event) + if (!isProtected || event.pubkey === pubkey) { + actions.push({ + icon: Globe, + label: t('Broadcast to ...'), + onClick: isSmallScreen + ? () => showSubMenuActions(broadcastSubMenu, t('Broadcast to ...')) + : undefined, + subMenu: isSmallScreen ? undefined : broadcastSubMenu, + separator: true + }) + } + + if (pubkey) { + if (isMuted) { + actions.push({ + icon: Bell, + label: t('Unmute user'), + onClick: () => { + closeDrawer() + unmutePubkey(event.pubkey) + }, + className: 'text-destructive focus:text-destructive', + separator: true + }) + } else { + actions.push( + { + icon: BellOff, + label: t('Mute user privately'), + onClick: () => { + closeDrawer() + mutePubkeyPrivately(event.pubkey) + }, + className: 'text-destructive focus:text-destructive', + separator: true + }, + { + icon: BellOff, + label: t('Mute user publicly'), + onClick: () => { + closeDrawer() + mutePubkeyPublicly(event.pubkey) + }, + className: 'text-destructive focus:text-destructive' + } + ) + } + } + + return actions + }, [ + t, + event, + pubkey, + isMuted, + isSmallScreen, + broadcastSubMenu, + closeDrawer, + showSubMenuActions, + setIsRawEventDialogOpen, + mutePubkeyPrivately, + mutePubkeyPublicly, + unmutePubkey + ]) + + return menuActions +} diff --git a/src/i18n/locales/ar.ts b/src/i18n/locales/ar.ts index 1c9eb41b..51062b18 100644 --- a/src/i18n/locales/ar.ts +++ b/src/i18n/locales/ar.ts @@ -314,6 +314,18 @@ export default { 'Remove poll': 'إزالة الاستطلاع', 'Refresh results': 'تحديث النتائج', Poll: 'استطلاع', - media: 'الوسائط' + media: 'الوسائط', + 'Broadcast to ...': 'البث إلى...', + 'Successfully broadcasted to your write relays': 'تم البث بنجاح إلى مرحلات الكتابة الخاصة بك', + 'Failed to broadcast to your write relays: {{error}}': + 'فشل البث إلى مرحلات الكتابة الخاصة بك: {{error}}', + 'Successfully broadcasted to relay set: {{name}}': + 'تم البث بنجاح إلى مجموعة المرحلات: {{name}}', + 'Failed to broadcast to relay set: {{name}}. Error: {{error}}': + 'فشل البث إلى مجموعة المرحلات: {{name}}. خطأ: {{error}}', + 'Successfully broadcasted to relay: {{url}}': 'تم البث بنجاح إلى المرحل: {{url}}', + 'Failed to broadcast to relay: {{url}}. Error: {{error}}': + 'فشل البث إلى المرحل: {{url}}. خطأ: {{error}}', + 'Write relays': 'مرحلات الكتابة' } } diff --git a/src/i18n/locales/de.ts b/src/i18n/locales/de.ts index 2c61e56d..9b786fa8 100644 --- a/src/i18n/locales/de.ts +++ b/src/i18n/locales/de.ts @@ -321,6 +321,18 @@ export default { 'Remove poll': 'Umfrage entfernen', 'Refresh results': 'Ergebnisse aktualisieren', Poll: 'Umfrage', - media: 'Medien' + media: 'Medien', + 'Broadcast to ...': 'Senden an...', + 'Successfully broadcasted to your write relays': 'Erfolgreich an Ihre Schreibrelays gesendet', + 'Failed to broadcast to your write relays: {{error}}': + 'Fehler beim Senden an Ihre Schreibrelays: {{error}}', + 'Successfully broadcasted to relay set: {{name}}': + 'Erfolgreich an Relay-Set gesendet: {{name}}', + 'Failed to broadcast to relay set: {{name}}. Error: {{error}}': + 'Fehler beim Senden an Relay-Set: {{name}}. Fehler: {{error}}', + 'Successfully broadcasted to relay: {{url}}': 'Erfolgreich an Relay gesendet: {{url}}', + 'Failed to broadcast to relay: {{url}}. Error: {{error}}': + 'Fehler beim Senden an Relay: {{url}}. Fehler: {{error}}', + 'Write relays': 'Schreib-Relays' } } diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index 62f5236b..12ae0e43 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -314,6 +314,19 @@ export default { 'Remove poll': 'Remove poll', 'Refresh results': 'Refresh results', Poll: 'Poll', - media: 'media' + media: 'media', + 'Broadcast to ...': 'Broadcast to ...', + 'Successfully broadcasted to your write relays': + 'Successfully broadcasted to your write relays', + 'Failed to broadcast to your write relays: {{error}}': + 'Failed to broadcast to your write relays: {{error}}', + 'Successfully broadcasted to relay set: {{name}}': + 'Successfully broadcasted to relay set: {{name}}', + 'Failed to broadcast to relay set: {{name}}. Error: {{error}}': + 'Failed to broadcast to relay set: {{name}}. Error: {{error}}', + 'Successfully broadcasted to relay: {{url}}': 'Successfully broadcasted to relay: {{url}}', + 'Failed to broadcast to relay: {{url}}. Error: {{error}}': + 'Failed to broadcast to relay: {{url}}. Error: {{error}}', + 'Write relays': 'Write relays' } } diff --git a/src/i18n/locales/es.ts b/src/i18n/locales/es.ts index d886f478..9b078525 100644 --- a/src/i18n/locales/es.ts +++ b/src/i18n/locales/es.ts @@ -319,6 +319,19 @@ export default { 'Remove poll': 'Eliminar encuesta', 'Refresh results': 'Actualizar resultados', Poll: 'Encuesta', - media: 'medios' + media: 'medios', + 'Broadcast to ...': 'Transmitir a...', + 'Successfully broadcasted to your write relays': + 'Transmitido exitosamente a sus relés de escritura', + 'Failed to broadcast to your write relays: {{error}}': + 'Error al transmitir a sus relés de escritura: {{error}}', + 'Successfully broadcasted to relay set: {{name}}': + 'Transmitido exitosamente al conjunto de relés: {{name}}', + 'Failed to broadcast to relay set: {{name}}. Error: {{error}}': + 'Error al transmitir al conjunto de relés: {{name}}. Error: {{error}}', + 'Successfully broadcasted to relay: {{url}}': 'Transmitido exitosamente al relé: {{url}}', + 'Failed to broadcast to relay: {{url}}. Error: {{error}}': + 'Error al transmitir al relé: {{url}}. Error: {{error}}', + 'Write relays': 'Relés de escritura' } } diff --git a/src/i18n/locales/fa.ts b/src/i18n/locales/fa.ts index a3e6ed76..f5b292b7 100644 --- a/src/i18n/locales/fa.ts +++ b/src/i18n/locales/fa.ts @@ -316,6 +316,17 @@ export default { 'Remove poll': 'حذف نظرسنجی', 'Refresh results': 'بارگیری مجدد نتایج', Poll: 'نظرسنجی', - media: 'رسانه' + media: 'رسانه', + 'Broadcast to ...': 'پخش به...', + 'Successfully broadcasted to your write relays': 'با موفقیت به رله‌های نوشتن شما پخش شد', + 'Failed to broadcast to your write relays: {{error}}': + 'پخش به رله‌های نوشتن شما ناموفق بود: {{error}}', + 'Successfully broadcasted to relay set: {{name}}': 'با موفقیت به مجموعه رله پخش شد: {{name}}', + 'Failed to broadcast to relay set: {{name}}. Error: {{error}}': + 'پخش به مجموعه رله ناموفق بود: {{name}}. خطا: {{error}}', + 'Successfully broadcasted to relay: {{url}}': 'با موفقیت به رله پخش شد: {{url}}', + 'Failed to broadcast to relay: {{url}}. Error: {{error}}': + 'پخش به رله ناموفق بود: {{url}}. خطا: {{error}}', + 'Write relays': 'رله‌های نوشتن' } } diff --git a/src/i18n/locales/fr.ts b/src/i18n/locales/fr.ts index ac1067c0..a5f148ee 100644 --- a/src/i18n/locales/fr.ts +++ b/src/i18n/locales/fr.ts @@ -320,6 +320,18 @@ export default { 'Remove poll': 'Supprimer le sondage', 'Refresh results': 'Rafraîchir les résultats', Poll: 'Sondage', - media: 'média' + media: 'média', + 'Broadcast to ...': 'Diffuser vers...', + 'Successfully broadcasted to your write relays': "Diffusion réussie vers vos relais d'écriture", + 'Failed to broadcast to your write relays: {{error}}': + "Échec de la diffusion vers vos relais d'écriture : {{error}}", + 'Successfully broadcasted to relay set: {{name}}': + "Diffusion réussie vers l'ensemble de relais : {{name}}", + 'Failed to broadcast to relay set: {{name}}. Error: {{error}}': + "Échec de la diffusion vers l'ensemble de relais : {{name}}. Erreur : {{error}}", + 'Successfully broadcasted to relay: {{url}}': 'Diffusion réussie vers le relais : {{url}}', + 'Failed to broadcast to relay: {{url}}. Error: {{error}}': + 'Échec de la diffusion vers le relais : {{url}}. Erreur : {{error}}', + 'Write relays': 'Relais d’écriture' } } diff --git a/src/i18n/locales/it.ts b/src/i18n/locales/it.ts index 4243debc..94aad998 100644 --- a/src/i18n/locales/it.ts +++ b/src/i18n/locales/it.ts @@ -318,6 +318,19 @@ export default { 'Remove poll': 'Rimuovi sondaggio', 'Refresh results': 'Aggiorna risultati', Poll: 'Sondaggio', - media: 'media' + media: 'media', + 'Broadcast to ...': 'Trasmetti a...', + 'Successfully broadcasted to your write relays': + 'Trasmesso con successo ai tuoi relay di scrittura', + 'Failed to broadcast to your write relays: {{error}}': + 'Errore nella trasmissione ai tuoi relay di scrittura: {{error}}', + 'Successfully broadcasted to relay set: {{name}}': + 'Trasmesso con successo al set di relay: {{name}}', + 'Failed to broadcast to relay set: {{name}}. Error: {{error}}': + 'Errore nella trasmissione al set di relay: {{name}}. Errore: {{error}}', + 'Successfully broadcasted to relay: {{url}}': 'Trasmesso con successo al relay: {{url}}', + 'Failed to broadcast to relay: {{url}}. Error: {{error}}': + 'Errore nella trasmissione al relay: {{url}}. Errore: {{error}}', + 'Write relays': 'Relay di scrittura' } } diff --git a/src/i18n/locales/ja.ts b/src/i18n/locales/ja.ts index d5c26b87..f273923b 100644 --- a/src/i18n/locales/ja.ts +++ b/src/i18n/locales/ja.ts @@ -316,6 +316,19 @@ export default { 'Remove poll': '投票を削除', 'Refresh results': '結果を更新', Poll: '投票', - media: 'メディア' + media: 'メディア', + 'Broadcast to ...': 'ブロードキャスト先...', + 'Successfully broadcasted to your write relays': '書きリレイへのブロードキャストが成功しました', + 'Failed to broadcast to your write relays: {{error}}': + '書きリレイへのブロードキャストが失敗しました:{{error}}', + 'Successfully broadcasted to relay set: {{name}}': + 'リレイセットへのブロードキャストが成功しました:{{name}}', + 'Failed to broadcast to relay set: {{name}}. Error: {{error}}': + 'リレイセットへのブロードキャストが失敗しました:{{name}}。エラー:{{error}}', + 'Successfully broadcasted to relay: {{url}}': + 'リレイへのブロードキャストが成功しました:{{url}}', + 'Failed to broadcast to relay: {{url}}. Error: {{error}}': + 'リレイへのブロードキャストが失敗しました:{{url}}。エラー:{{error}}', + 'Write relays': '書きリレイ' } } diff --git a/src/i18n/locales/ko.ts b/src/i18n/locales/ko.ts index 4609aee8..34123c5b 100644 --- a/src/i18n/locales/ko.ts +++ b/src/i18n/locales/ko.ts @@ -316,6 +316,18 @@ export default { 'Remove poll': '투표 제거', 'Refresh results': '결과 새로 고침', Poll: '투표', - media: '미디어' + media: '미디어', + 'Broadcast to ...': '브로드캐스트 대상...', + 'Successfully broadcasted to your write relays': '쓰기 릴레이로 브로드캐스트에 성공했습니다', + 'Failed to broadcast to your write relays: {{error}}': + '쓰기 릴레이로 브로드캐스트에 실패했습니다: {{error}}', + 'Successfully broadcasted to relay set: {{name}}': + '릴레이 세트로 브로드캐스트에 성공했습니다: {{name}}', + 'Failed to broadcast to relay set: {{name}}. Error: {{error}}': + '릴레이 세트로 브로드캐스트에 실패했습니다: {{name}}. 오류: {{error}}', + 'Successfully broadcasted to relay: {{url}}': '릴레이로 브로드캐스트에 성공했습니다: {{url}}', + 'Failed to broadcast to relay: {{url}}. Error: {{error}}': + '릴레이로 브로드캐스트에 실패했습니다: {{url}}. 오류: {{error}}', + 'Write relays': '쓰기 릴레이' } } diff --git a/src/i18n/locales/pl.ts b/src/i18n/locales/pl.ts index 9eef6316..9021545a 100644 --- a/src/i18n/locales/pl.ts +++ b/src/i18n/locales/pl.ts @@ -318,6 +318,19 @@ export default { 'Remove poll': 'Usuń ankietę', 'Refresh results': 'Odśwież wyniki', Poll: 'Ankieta', - media: 'media' + media: 'media', + 'Broadcast to ...': 'Transmituj do...', + 'Successfully broadcasted to your write relays': + 'Pomyślnie transmitowano do twoich przekaźników zapisu', + 'Failed to broadcast to your write relays: {{error}}': + 'Nie udało się transmitować do twoich przekaźników zapisu: {{error}}', + 'Successfully broadcasted to relay set: {{name}}': + 'Pomyślnie transmitowano do zestawu przekaźników: {{name}}', + 'Failed to broadcast to relay set: {{name}}. Error: {{error}}': + 'Nie udało się transmitować do zestawu przekaźników: {{name}}. Błąd: {{error}}', + 'Successfully broadcasted to relay: {{url}}': 'Pomyślnie transmitowano do przekaźnika: {{url}}', + 'Failed to broadcast to relay: {{url}}. Error: {{error}}': + 'Nie udało się transmitować do przekaźnika: {{url}}. Błąd: {{error}}', + 'Write relays': 'Przekaźniki zapisu' } } diff --git a/src/i18n/locales/pt-BR.ts b/src/i18n/locales/pt-BR.ts index dbec6a2a..6298e1ab 100644 --- a/src/i18n/locales/pt-BR.ts +++ b/src/i18n/locales/pt-BR.ts @@ -317,6 +317,19 @@ export default { 'Remove poll': 'Remover enquete', 'Refresh results': 'Atualizar resultados', Poll: 'Enquete', - media: 'Mídia' + media: 'Mídia', + 'Broadcast to ...': 'Transmitir para...', + 'Successfully broadcasted to your write relays': + 'Transmitido com sucesso para seus relays de escrita', + 'Failed to broadcast to your write relays: {{error}}': + 'Falha ao transmitir para seus relays de escrita: {{error}}', + 'Successfully broadcasted to relay set: {{name}}': + 'Transmitido com sucesso para o conjunto de relays: {{name}}', + 'Failed to broadcast to relay set: {{name}}. Error: {{error}}': + 'Falha ao transmitir para o conjunto de relays: {{name}}. Erro: {{error}}', + 'Successfully broadcasted to relay: {{url}}': 'Transmitido com sucesso para o relay: {{url}}', + 'Failed to broadcast to relay: {{url}}. Error: {{error}}': + 'Falha ao transmitir para o relay: {{url}}. Erro: {{error}}', + 'Write relays': 'Relés de escrita' } } diff --git a/src/i18n/locales/pt-PT.ts b/src/i18n/locales/pt-PT.ts index 1018af31..1ad1c88a 100644 --- a/src/i18n/locales/pt-PT.ts +++ b/src/i18n/locales/pt-PT.ts @@ -318,6 +318,19 @@ export default { 'Remove poll': 'Remover sondagem', 'Refresh results': 'Atualizar resultados', Poll: 'Sondagem', - media: 'mídia' + media: 'mídia', + 'Broadcast to ...': 'Transmitir para...', + 'Successfully broadcasted to your write relays': + 'Transmitido com sucesso para os seus relays de escrita', + 'Failed to broadcast to your write relays: {{error}}': + 'Falha ao transmitir para os seus relays de escrita: {{error}}', + 'Successfully broadcasted to relay set: {{name}}': + 'Transmitido com sucesso para o conjunto de relays: {{name}}', + 'Failed to broadcast to relay set: {{name}}. Error: {{error}}': + 'Falha ao transmitir para o conjunto de relays: {{name}}. Erro: {{error}}', + 'Successfully broadcasted to relay: {{url}}': 'Transmitido com sucesso para o relay: {{url}}', + 'Failed to broadcast to relay: {{url}}. Error: {{error}}': + 'Falha ao transmitir para o relay: {{url}}. Erro: {{error}}', + 'Write relays': 'Relés de escrita' } } diff --git a/src/i18n/locales/ru.ts b/src/i18n/locales/ru.ts index a7ce57ad..669a723e 100644 --- a/src/i18n/locales/ru.ts +++ b/src/i18n/locales/ru.ts @@ -309,7 +309,7 @@ export default { 'Load results': 'Загрузить результаты', 'This is a poll note.': 'Это заметка с опросом.', 'Unlike regular notes, polls are not widely supported and may not display on other clients.': - 'В отличие от обычных заметок, опросы не получили широкую поддержку и могут не отображаться в других клиентах.', + 'В отличие от обычных заметок, опросы не получили широкой поддержки и могут не отображаться в других клиентах.', 'Option {{number}}': 'Вариант {{number}}', 'Add Option': 'Добавить вариант', 'Allow multiple choices': 'Разрешить множественный выбор', @@ -319,6 +319,18 @@ export default { 'Remove poll': 'Удалить опрос', 'Refresh results': 'Обновить результаты', Poll: 'Опрос', - media: 'медиа' + media: 'медиа', + 'Broadcast to ...': 'Транслировать в...', + 'Successfully broadcasted to your write relays': 'Успешно транслировано в ваши релеи записи', + 'Failed to broadcast to your write relays: {{error}}': + 'Ошибка трансляции в ваши релеи записи: {{error}}', + 'Successfully broadcasted to relay set: {{name}}': + 'Успешно транслировано в набор релеев: {{name}}', + 'Failed to broadcast to relay set: {{name}}. Error: {{error}}': + 'Ошибка трансляции в набор релеев: {{name}}. Ошибка: {{error}}', + 'Successfully broadcasted to relay: {{url}}': 'Успешно транслировано в релей: {{url}}', + 'Failed to broadcast to relay: {{url}}. Error: {{error}}': + 'Ошибка трансляции в релей: {{url}}. Ошибка: {{error}}', + 'Write relays': 'Ретрансляторы для записи' } } diff --git a/src/i18n/locales/th.ts b/src/i18n/locales/th.ts index 69add39d..133a9fc0 100644 --- a/src/i18n/locales/th.ts +++ b/src/i18n/locales/th.ts @@ -313,6 +313,18 @@ export default { 'Remove poll': 'ลบโพลล์', 'Refresh results': 'รีเฟรชผลลัพธ์', Poll: 'โพลล์', - media: 'สื่อ' + media: 'สื่อ', + 'Broadcast to ...': 'ส่งสัญญาณไปยัง...', + 'Successfully broadcasted to your write relays': 'ส่งสัญญาณไปยังรีเลย์การเขียนของคุณสำเร็จแล้ว', + 'Failed to broadcast to your write relays: {{error}}': + 'การส่งสัญญาณไปยังรีเลย์การเขียนของคุณล้มเหลว: {{error}}', + 'Successfully broadcasted to relay set: {{name}}': + 'ส่งสัญญาณไปยังชุดรีเลย์สำเร็จแล้ว: {{name}}', + 'Failed to broadcast to relay set: {{name}}. Error: {{error}}': + 'การส่งสัญญาณไปยังชุดรีเลย์ล้มเหลว: {{name}} ข้อผิดพลาด: {{error}}', + 'Successfully broadcasted to relay: {{url}}': 'ส่งสัญญาณไปยังรีเลย์สำเร็จแล้ว: {{url}}', + 'Failed to broadcast to relay: {{url}}. Error: {{error}}': + 'การส่งสัญญาณไปยังรีเลย์ล้มเหลว: {{url}} ข้อผิดพลาด: {{error}}', + 'Write relays': 'รีเลย์การเขียน' } } diff --git a/src/i18n/locales/zh.ts b/src/i18n/locales/zh.ts index 6979ff30..caa2e9d4 100644 --- a/src/i18n/locales/zh.ts +++ b/src/i18n/locales/zh.ts @@ -314,6 +314,16 @@ export default { 'Remove poll': '移除投票', 'Refresh results': '刷新结果', Poll: '投票', - media: '媒体' + media: '媒体', + 'Broadcast to ...': '广播到...', + 'Successfully broadcasted to your write relays': '成功广播到您的写服务器', + 'Failed to broadcast to your write relays: {{error}}': '广播到您的写服务器失败:{{error}}', + 'Successfully broadcasted to relay set: {{name}}': '成功广播到服务器组:{{name}}', + 'Failed to broadcast to relay set: {{name}}. Error: {{error}}': + '广播到服务器组失败:{{name}}。错误:{{error}}', + 'Successfully broadcasted to relay: {{url}}': '成功广播到服务器:{{url}}', + 'Failed to broadcast to relay: {{url}}. Error: {{error}}': + '广播到服务器失败:{{url}}。错误:{{error}}', + 'Write relays': '写服务器' } } diff --git a/src/services/client.service.ts b/src/services/client.service.ts index 81ca37fe..da245dd5 100644 --- a/src/services/client.service.ts +++ b/src/services/client.service.ts @@ -115,35 +115,42 @@ class ClientService extends EventTarget { } async publishEvent(relayUrls: string[], event: NEvent) { - const uniqueRelayUrls = Array.from(new Set(relayUrls)) - const result = await Promise.any( - uniqueRelayUrls.map(async (url) => { - // eslint-disable-next-line @typescript-eslint/no-this-alias - const that = this - const relay = await this.pool.ensureRelay(url) - return relay - .publish(event) - .catch((error) => { - if ( - error instanceof Error && - error.message.startsWith('auth-required') && - !!that.signer - ) { - return relay - .auth((authEvt: EventTemplate) => that.signer!.signEvent(authEvt)) - .then(() => relay.publish(event)) - } else { - throw error - } - }) - .then((reason) => { - this.trackEventSeenOn(event.id, relay) - return reason - }) - }) - ) - this.dispatchEvent(new CustomEvent('eventPublished', { detail: event })) - return result + try { + const uniqueRelayUrls = Array.from(new Set(relayUrls)) + const result = await Promise.any( + uniqueRelayUrls.map(async (url) => { + // eslint-disable-next-line @typescript-eslint/no-this-alias + const that = this + const relay = await this.pool.ensureRelay(url) + return relay + .publish(event) + .catch((error) => { + if ( + error instanceof Error && + error.message.startsWith('auth-required') && + !!that.signer + ) { + return relay + .auth((authEvt: EventTemplate) => that.signer!.signEvent(authEvt)) + .then(() => relay.publish(event)) + } else { + throw error + } + }) + .then((reason) => { + this.trackEventSeenOn(event.id, relay) + return reason + }) + }) + ) + this.dispatchEvent(new CustomEvent('eventPublished', { detail: event })) + return result + } catch (error) { + if (error instanceof AggregateError) { + throw error.errors[0] + } + throw error + } } async signHttpAuth(url: string, method: string, description = '') {