import { useState, useCallback } from 'react'; import { useTranslation } from 'react-i18next'; import { CheckIcon, CopyIcon } from '@/components/icons'; import type { RemnawaveButtonClient, LocalizedText } from '@/types'; import { copyToClipboard } from '@/utils/clipboard'; import { blockButtonClass } from './buttonStyles'; // eslint-disable-next-line no-script-url const dangerousSchemes = ['javascript:', 'data:', 'vbscript:', 'file:']; function isValidDeepLink(url: string | undefined): boolean { if (!url) return false; const lowerUrl = url.toLowerCase().trim(); if (dangerousSchemes.some((s) => lowerUrl.startsWith(s))) return false; return lowerUrl.includes('://'); } function isValidExternalUrl(url: string | undefined): boolean { if (!url) return false; const lowerUrl = url.toLowerCase().trim(); if (dangerousSchemes.some((s) => lowerUrl.startsWith(s))) return false; return lowerUrl.startsWith('http://') || lowerUrl.startsWith('https://'); } interface BlockButtonsProps { buttons: RemnawaveButtonClient[] | undefined; variant: 'light' | 'subtle'; isLight?: boolean; subscriptionUrl: string | null; hideLink?: boolean; deepLink?: string | null; getLocalizedText: (text: LocalizedText | undefined) => string; getBaseTranslation: (key: string, i18nKey: string) => string; getSvgHtml: (key: string | undefined) => string; onOpenDeepLink: (url: string) => void; } export function BlockButtons({ buttons, variant, isLight, subscriptionUrl, hideLink, deepLink, getLocalizedText, getBaseTranslation, getSvgHtml, onOpenDeepLink, }: BlockButtonsProps) { const { t } = useTranslation(); const [copied, setCopied] = useState(false); const handleCopy = useCallback(async (url: string) => { await copyToClipboard(url); setCopied(true); setTimeout(() => setCopied(false), 2000); }, []); if (!buttons || buttons.length === 0) return null; const baseClass = blockButtonClass(variant, isLight); return (
{buttons.map((btn, idx) => { const btnText = getLocalizedText(btn.text); const btnSvg = getSvgHtml(btn.svgIconKey); const btnIcon = btnSvg ? (
) : null; if (btn.type === 'subscriptionLink') { const url = btn.resolvedUrl || btn.url || btn.link || deepLink || subscriptionUrl; if (!url || !isValidDeepLink(url)) return null; return ( ); } if (btn.type === 'copyButton') { if (hideLink) return null; const url = btn.resolvedUrl || subscriptionUrl; if (!url) return null; return ( ); } // external const href = btn.link || btn.url || ''; if (!isValidExternalUrl(href)) return null; return ( {btnIcon} {btnText} ); })}
); }