feat: broadcast
This commit is contained in:
62
src/components/NoteOptions/DesktopMenu.tsx
Normal file
62
src/components/NoteOptions/DesktopMenu.tsx
Normal file
@@ -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 (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent className="max-h-screen overflow-y-auto">
|
||||||
|
{menuActions.map((action, index) => {
|
||||||
|
const Icon = action.icon
|
||||||
|
return (
|
||||||
|
<div key={index}>
|
||||||
|
{action.separator && index > 0 && <DropdownMenuSeparator />}
|
||||||
|
{action.subMenu ? (
|
||||||
|
<DropdownMenuSub>
|
||||||
|
<DropdownMenuSubTrigger className={action.className}>
|
||||||
|
<Icon />
|
||||||
|
{action.label}
|
||||||
|
</DropdownMenuSubTrigger>
|
||||||
|
<DropdownMenuSubContent className="max-h-screen overflow-y-auto">
|
||||||
|
{action.subMenu.map((subAction, subIndex) => (
|
||||||
|
<>
|
||||||
|
{subAction.separator && subIndex > 0 && <DropdownMenuSeparator />}
|
||||||
|
<DropdownMenuItem
|
||||||
|
key={subIndex}
|
||||||
|
onClick={subAction.onClick}
|
||||||
|
className={cn('w-64', subAction.className)}
|
||||||
|
>
|
||||||
|
{subAction.label}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</DropdownMenuSubContent>
|
||||||
|
</DropdownMenuSub>
|
||||||
|
) : (
|
||||||
|
<DropdownMenuItem onClick={action.onClick} className={action.className}>
|
||||||
|
<Icon />
|
||||||
|
{action.label}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
)
|
||||||
|
}
|
||||||
79
src/components/NoteOptions/MobileMenu.tsx
Normal file
79
src/components/NoteOptions/MobileMenu.tsx
Normal file
@@ -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}
|
||||||
|
<Drawer open={isDrawerOpen} onOpenChange={setIsDrawerOpen}>
|
||||||
|
<DrawerOverlay onClick={closeDrawer} />
|
||||||
|
<DrawerContent hideOverlay className="max-h-screen">
|
||||||
|
<div className="overflow-y-auto overscroll-contain py-2" style={{ touchAction: 'pan-y' }}>
|
||||||
|
{!showSubMenu ? (
|
||||||
|
menuActions.map((action, index) => {
|
||||||
|
const Icon = action.icon
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
key={index}
|
||||||
|
onClick={action.onClick}
|
||||||
|
className={`w-full p-6 justify-start text-lg gap-4 [&_svg]:size-5 ${action.className || ''}`}
|
||||||
|
variant="ghost"
|
||||||
|
>
|
||||||
|
<Icon />
|
||||||
|
{action.label}
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
onClick={goBackToMainMenu}
|
||||||
|
className="w-full p-6 justify-start text-lg gap-4 [&_svg]:size-5 mb-2"
|
||||||
|
variant="ghost"
|
||||||
|
>
|
||||||
|
<ArrowLeft />
|
||||||
|
{subMenuTitle}
|
||||||
|
</Button>
|
||||||
|
<div className="border-t border-border mb-2" />
|
||||||
|
{activeSubMenu.map((subAction, index) => (
|
||||||
|
<Button
|
||||||
|
key={index}
|
||||||
|
onClick={subAction.onClick}
|
||||||
|
className={`w-full p-6 justify-start text-lg gap-4 ${subAction.className || ''}`}
|
||||||
|
variant="ghost"
|
||||||
|
>
|
||||||
|
{subAction.label}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</DrawerContent>
|
||||||
|
</Drawer>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -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 { 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 { Event } from 'nostr-tools'
|
||||||
import { useMemo, useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { DesktopMenu } from './DesktopMenu'
|
||||||
|
import { MobileMenu } from './MobileMenu'
|
||||||
import RawEventDialog from './RawEventDialog'
|
import RawEventDialog from './RawEventDialog'
|
||||||
|
import { SubMenuAction, useMenuActions } from './useMenuActions'
|
||||||
|
|
||||||
export default function NoteOptions({ event, className }: { event: Event; className?: string }) {
|
export default function NoteOptions({ event, className }: { event: Event; className?: string }) {
|
||||||
const { t } = useTranslation()
|
|
||||||
const { isSmallScreen } = useScreenSize()
|
const { isSmallScreen } = useScreenSize()
|
||||||
const { pubkey } = useNostr()
|
|
||||||
const [isRawEventDialogOpen, setIsRawEventDialogOpen] = useState(false)
|
const [isRawEventDialogOpen, setIsRawEventDialogOpen] = useState(false)
|
||||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false)
|
const [isDrawerOpen, setIsDrawerOpen] = useState(false)
|
||||||
const { mutePubkeyPublicly, mutePubkeyPrivately, unmutePubkey, mutePubkeys } = useMuteList()
|
const [showSubMenu, setShowSubMenu] = useState(false)
|
||||||
const isMuted = useMemo(() => mutePubkeys.includes(event.pubkey), [mutePubkeys, event])
|
const [activeSubMenu, setActiveSubMenu] = useState<SubMenuAction[]>([])
|
||||||
|
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 = (
|
const trigger = (
|
||||||
<button
|
<button
|
||||||
@@ -37,175 +47,29 @@ export default function NoteOptions({ event, className }: { event: Event; classN
|
|||||||
</button>
|
</button>
|
||||||
)
|
)
|
||||||
|
|
||||||
const rawEventDialog = (
|
|
||||||
<RawEventDialog
|
|
||||||
event={event}
|
|
||||||
isOpen={isRawEventDialogOpen}
|
|
||||||
onClose={() => setIsRawEventDialogOpen(false)}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
|
|
||||||
if (isSmallScreen) {
|
|
||||||
return (
|
|
||||||
<div className={className} onClick={(e) => e.stopPropagation()}>
|
|
||||||
{trigger}
|
|
||||||
<Drawer open={isDrawerOpen} onOpenChange={setIsDrawerOpen}>
|
|
||||||
<DrawerOverlay onClick={() => setIsDrawerOpen(false)} />
|
|
||||||
<DrawerContent hideOverlay>
|
|
||||||
<div className="py-2">
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
setIsDrawerOpen(false)
|
|
||||||
navigator.clipboard.writeText(getNoteBech32Id(event))
|
|
||||||
}}
|
|
||||||
className="w-full p-6 justify-start text-lg gap-4 [&_svg]:size-5"
|
|
||||||
variant="ghost"
|
|
||||||
>
|
|
||||||
<Copy />
|
|
||||||
{t('Copy event ID')}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
navigator.clipboard.writeText(pubkeyToNpub(event.pubkey) ?? '')
|
|
||||||
setIsDrawerOpen(false)
|
|
||||||
}}
|
|
||||||
className="w-full p-6 justify-start text-lg gap-4 [&_svg]:size-5"
|
|
||||||
variant="ghost"
|
|
||||||
>
|
|
||||||
<Copy />
|
|
||||||
{t('Copy user ID')}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
setIsDrawerOpen(false)
|
|
||||||
navigator.clipboard.writeText(toNjump(getNoteBech32Id(event)))
|
|
||||||
}}
|
|
||||||
className="w-full p-6 justify-start text-lg gap-4 [&_svg]:size-5"
|
|
||||||
variant="ghost"
|
|
||||||
>
|
|
||||||
<Link />
|
|
||||||
{t('Copy share link')}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
setIsDrawerOpen(false)
|
|
||||||
setIsRawEventDialogOpen(true)
|
|
||||||
}}
|
|
||||||
className="w-full p-6 justify-start text-lg gap-4 [&_svg]:size-5"
|
|
||||||
variant="ghost"
|
|
||||||
>
|
|
||||||
<Code />
|
|
||||||
{t('View raw event')}
|
|
||||||
</Button>
|
|
||||||
{pubkey &&
|
|
||||||
(isMuted ? (
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
setIsDrawerOpen(false)
|
|
||||||
unmutePubkey(event.pubkey)
|
|
||||||
}}
|
|
||||||
className="w-full p-6 justify-start text-destructive text-lg gap-4 [&_svg]:size-5 focus:text-destructive"
|
|
||||||
variant="ghost"
|
|
||||||
>
|
|
||||||
<Bell />
|
|
||||||
{t('Unmute user')}
|
|
||||||
</Button>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
setIsDrawerOpen(false)
|
|
||||||
mutePubkeyPrivately(event.pubkey)
|
|
||||||
}}
|
|
||||||
className="w-full p-6 justify-start text-destructive text-lg gap-4 [&_svg]:size-5 focus:text-destructive"
|
|
||||||
variant="ghost"
|
|
||||||
>
|
|
||||||
<BellOff />
|
|
||||||
{t('Mute user privately')}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
onClick={() => {
|
|
||||||
setIsDrawerOpen(false)
|
|
||||||
mutePubkeyPublicly(event.pubkey)
|
|
||||||
}}
|
|
||||||
className="w-full p-6 justify-start text-destructive text-lg gap-4 [&_svg]:size-5 focus:text-destructive"
|
|
||||||
variant="ghost"
|
|
||||||
>
|
|
||||||
<BellOff />
|
|
||||||
{t('Mute user publicly')}
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</DrawerContent>
|
|
||||||
</Drawer>
|
|
||||||
{rawEventDialog}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className} onClick={(e) => e.stopPropagation()}>
|
<div className={className} onClick={(e) => e.stopPropagation()}>
|
||||||
<DropdownMenu>
|
{isSmallScreen ? (
|
||||||
<DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
|
<MobileMenu
|
||||||
<DropdownMenuContent>
|
menuActions={menuActions}
|
||||||
<DropdownMenuItem onClick={() => navigator.clipboard.writeText(getNoteBech32Id(event))}>
|
trigger={trigger}
|
||||||
<Copy />
|
isDrawerOpen={isDrawerOpen}
|
||||||
{t('Copy event ID')}
|
setIsDrawerOpen={setIsDrawerOpen}
|
||||||
</DropdownMenuItem>
|
showSubMenu={showSubMenu}
|
||||||
<DropdownMenuItem
|
activeSubMenu={activeSubMenu}
|
||||||
onClick={() => navigator.clipboard.writeText(pubkeyToNpub(event.pubkey) ?? '')}
|
subMenuTitle={subMenuTitle}
|
||||||
>
|
closeDrawer={closeDrawer}
|
||||||
<Copy />
|
goBackToMainMenu={goBackToMainMenu}
|
||||||
{t('Copy user ID')}
|
/>
|
||||||
</DropdownMenuItem>
|
) : (
|
||||||
<DropdownMenuItem
|
<DesktopMenu menuActions={menuActions} trigger={trigger} />
|
||||||
onClick={() => navigator.clipboard.writeText(toNjump(getNoteBech32Id(event)))}
|
)}
|
||||||
>
|
|
||||||
<Link />
|
|
||||||
{t('Copy share link')}
|
|
||||||
</DropdownMenuItem>
|
|
||||||
|
|
||||||
<DropdownMenuSeparator />
|
<RawEventDialog
|
||||||
|
event={event}
|
||||||
<DropdownMenuItem onClick={() => setIsRawEventDialogOpen(true)}>
|
isOpen={isRawEventDialogOpen}
|
||||||
<Code />
|
onClose={() => setIsRawEventDialogOpen(false)}
|
||||||
{t('View raw event')}
|
/>
|
||||||
</DropdownMenuItem>
|
|
||||||
{pubkey && (
|
|
||||||
<>
|
|
||||||
<DropdownMenuSeparator />
|
|
||||||
{isMuted ? (
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={() => unmutePubkey(event.pubkey)}
|
|
||||||
className="text-destructive focus:text-destructive"
|
|
||||||
>
|
|
||||||
<Bell />
|
|
||||||
{t('Unmute user')}
|
|
||||||
</DropdownMenuItem>
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={() => mutePubkeyPrivately(event.pubkey)}
|
|
||||||
className="text-destructive focus:text-destructive"
|
|
||||||
>
|
|
||||||
<BellOff />
|
|
||||||
{t('Mute user privately')}
|
|
||||||
</DropdownMenuItem>
|
|
||||||
<DropdownMenuItem
|
|
||||||
onClick={() => mutePubkeyPublicly(event.pubkey)}
|
|
||||||
className="text-destructive focus:text-destructive"
|
|
||||||
>
|
|
||||||
<BellOff />
|
|
||||||
{t('Mute user publicly')}
|
|
||||||
</DropdownMenuItem>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</DropdownMenuContent>
|
|
||||||
</DropdownMenu>
|
|
||||||
{rawEventDialog}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
255
src/components/NoteOptions/useMenuActions.tsx
Normal file
255
src/components/NoteOptions/useMenuActions.tsx
Normal file
@@ -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: (
|
||||||
|
<div className="flex items-center gap-2 w-full pl-1">
|
||||||
|
<Mail />
|
||||||
|
<div className="flex-1 truncate text-left">{t('Write relays')}</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
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: (
|
||||||
|
<div className="flex items-center gap-2 w-full pl-1">
|
||||||
|
<Server />
|
||||||
|
<div className="flex-1 truncate text-left">{set.name}</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
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: (
|
||||||
|
<div className="flex items-center gap-2 w-full">
|
||||||
|
<RelayIcon url={relay} />
|
||||||
|
<div className="flex-1 truncate text-left">{simplifyUrl(relay)}</div>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -314,6 +314,18 @@ export default {
|
|||||||
'Remove poll': 'إزالة الاستطلاع',
|
'Remove poll': 'إزالة الاستطلاع',
|
||||||
'Refresh results': 'تحديث النتائج',
|
'Refresh results': 'تحديث النتائج',
|
||||||
Poll: 'استطلاع',
|
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': 'مرحلات الكتابة'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -321,6 +321,18 @@ export default {
|
|||||||
'Remove poll': 'Umfrage entfernen',
|
'Remove poll': 'Umfrage entfernen',
|
||||||
'Refresh results': 'Ergebnisse aktualisieren',
|
'Refresh results': 'Ergebnisse aktualisieren',
|
||||||
Poll: 'Umfrage',
|
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'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -314,6 +314,19 @@ export default {
|
|||||||
'Remove poll': 'Remove poll',
|
'Remove poll': 'Remove poll',
|
||||||
'Refresh results': 'Refresh results',
|
'Refresh results': 'Refresh results',
|
||||||
Poll: 'Poll',
|
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'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -319,6 +319,19 @@ export default {
|
|||||||
'Remove poll': 'Eliminar encuesta',
|
'Remove poll': 'Eliminar encuesta',
|
||||||
'Refresh results': 'Actualizar resultados',
|
'Refresh results': 'Actualizar resultados',
|
||||||
Poll: 'Encuesta',
|
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'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -316,6 +316,17 @@ export default {
|
|||||||
'Remove poll': 'حذف نظرسنجی',
|
'Remove poll': 'حذف نظرسنجی',
|
||||||
'Refresh results': 'بارگیری مجدد نتایج',
|
'Refresh results': 'بارگیری مجدد نتایج',
|
||||||
Poll: 'نظرسنجی',
|
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': 'رلههای نوشتن'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -320,6 +320,18 @@ export default {
|
|||||||
'Remove poll': 'Supprimer le sondage',
|
'Remove poll': 'Supprimer le sondage',
|
||||||
'Refresh results': 'Rafraîchir les résultats',
|
'Refresh results': 'Rafraîchir les résultats',
|
||||||
Poll: 'Sondage',
|
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'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -318,6 +318,19 @@ export default {
|
|||||||
'Remove poll': 'Rimuovi sondaggio',
|
'Remove poll': 'Rimuovi sondaggio',
|
||||||
'Refresh results': 'Aggiorna risultati',
|
'Refresh results': 'Aggiorna risultati',
|
||||||
Poll: 'Sondaggio',
|
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'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -316,6 +316,19 @@ export default {
|
|||||||
'Remove poll': '投票を削除',
|
'Remove poll': '投票を削除',
|
||||||
'Refresh results': '結果を更新',
|
'Refresh results': '結果を更新',
|
||||||
Poll: '投票',
|
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': '書きリレイ'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -316,6 +316,18 @@ export default {
|
|||||||
'Remove poll': '투표 제거',
|
'Remove poll': '투표 제거',
|
||||||
'Refresh results': '결과 새로 고침',
|
'Refresh results': '결과 새로 고침',
|
||||||
Poll: '투표',
|
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': '쓰기 릴레이'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -318,6 +318,19 @@ export default {
|
|||||||
'Remove poll': 'Usuń ankietę',
|
'Remove poll': 'Usuń ankietę',
|
||||||
'Refresh results': 'Odśwież wyniki',
|
'Refresh results': 'Odśwież wyniki',
|
||||||
Poll: 'Ankieta',
|
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'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -317,6 +317,19 @@ export default {
|
|||||||
'Remove poll': 'Remover enquete',
|
'Remove poll': 'Remover enquete',
|
||||||
'Refresh results': 'Atualizar resultados',
|
'Refresh results': 'Atualizar resultados',
|
||||||
Poll: 'Enquete',
|
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'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -318,6 +318,19 @@ export default {
|
|||||||
'Remove poll': 'Remover sondagem',
|
'Remove poll': 'Remover sondagem',
|
||||||
'Refresh results': 'Atualizar resultados',
|
'Refresh results': 'Atualizar resultados',
|
||||||
Poll: 'Sondagem',
|
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'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -309,7 +309,7 @@ export default {
|
|||||||
'Load results': 'Загрузить результаты',
|
'Load results': 'Загрузить результаты',
|
||||||
'This is a poll note.': 'Это заметка с опросом.',
|
'This is a poll note.': 'Это заметка с опросом.',
|
||||||
'Unlike regular notes, polls are not widely supported and may not display on other clients.':
|
'Unlike regular notes, polls are not widely supported and may not display on other clients.':
|
||||||
'В отличие от обычных заметок, опросы не получили широкую поддержку и могут не отображаться в других клиентах.',
|
'В отличие от обычных заметок, опросы не получили широкой поддержки и могут не отображаться в других клиентах.',
|
||||||
'Option {{number}}': 'Вариант {{number}}',
|
'Option {{number}}': 'Вариант {{number}}',
|
||||||
'Add Option': 'Добавить вариант',
|
'Add Option': 'Добавить вариант',
|
||||||
'Allow multiple choices': 'Разрешить множественный выбор',
|
'Allow multiple choices': 'Разрешить множественный выбор',
|
||||||
@@ -319,6 +319,18 @@ export default {
|
|||||||
'Remove poll': 'Удалить опрос',
|
'Remove poll': 'Удалить опрос',
|
||||||
'Refresh results': 'Обновить результаты',
|
'Refresh results': 'Обновить результаты',
|
||||||
Poll: 'Опрос',
|
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': 'Ретрансляторы для записи'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -313,6 +313,18 @@ export default {
|
|||||||
'Remove poll': 'ลบโพลล์',
|
'Remove poll': 'ลบโพลล์',
|
||||||
'Refresh results': 'รีเฟรชผลลัพธ์',
|
'Refresh results': 'รีเฟรชผลลัพธ์',
|
||||||
Poll: 'โพลล์',
|
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': 'รีเลย์การเขียน'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -314,6 +314,16 @@ export default {
|
|||||||
'Remove poll': '移除投票',
|
'Remove poll': '移除投票',
|
||||||
'Refresh results': '刷新结果',
|
'Refresh results': '刷新结果',
|
||||||
Poll: '投票',
|
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': '写服务器'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,35 +115,42 @@ class ClientService extends EventTarget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async publishEvent(relayUrls: string[], event: NEvent) {
|
async publishEvent(relayUrls: string[], event: NEvent) {
|
||||||
const uniqueRelayUrls = Array.from(new Set(relayUrls))
|
try {
|
||||||
const result = await Promise.any(
|
const uniqueRelayUrls = Array.from(new Set(relayUrls))
|
||||||
uniqueRelayUrls.map(async (url) => {
|
const result = await Promise.any(
|
||||||
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
uniqueRelayUrls.map(async (url) => {
|
||||||
const that = this
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||||
const relay = await this.pool.ensureRelay(url)
|
const that = this
|
||||||
return relay
|
const relay = await this.pool.ensureRelay(url)
|
||||||
.publish(event)
|
return relay
|
||||||
.catch((error) => {
|
.publish(event)
|
||||||
if (
|
.catch((error) => {
|
||||||
error instanceof Error &&
|
if (
|
||||||
error.message.startsWith('auth-required') &&
|
error instanceof Error &&
|
||||||
!!that.signer
|
error.message.startsWith('auth-required') &&
|
||||||
) {
|
!!that.signer
|
||||||
return relay
|
) {
|
||||||
.auth((authEvt: EventTemplate) => that.signer!.signEvent(authEvt))
|
return relay
|
||||||
.then(() => relay.publish(event))
|
.auth((authEvt: EventTemplate) => that.signer!.signEvent(authEvt))
|
||||||
} else {
|
.then(() => relay.publish(event))
|
||||||
throw error
|
} else {
|
||||||
}
|
throw error
|
||||||
})
|
}
|
||||||
.then((reason) => {
|
})
|
||||||
this.trackEventSeenOn(event.id, relay)
|
.then((reason) => {
|
||||||
return reason
|
this.trackEventSeenOn(event.id, relay)
|
||||||
})
|
return reason
|
||||||
})
|
})
|
||||||
)
|
})
|
||||||
this.dispatchEvent(new CustomEvent('eventPublished', { detail: event }))
|
)
|
||||||
return result
|
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 = '') {
|
async signHttpAuth(url: string, method: string, description = '') {
|
||||||
|
|||||||
Reference in New Issue
Block a user