import { useMemo, type ReactNode } from 'react';
import { createPortal } from 'react-dom';
import { useTranslation } from 'react-i18next';
interface PreviewButton {
text: string;
url?: string;
callback_data?: string;
}
interface TelegramPreviewProps {
open: boolean;
onClose: () => void;
text: string;
mediaUrl?: string | null;
mediaType?: 'photo' | 'video' | null;
buttons?: PreviewButton[][];
}
interface EmailPreviewProps {
open: boolean;
onClose: () => void;
subject: string;
htmlContent: string;
}
interface Token {
kind: 'text' | 'open' | 'close' | 'br';
tag?: string;
href?: string;
value?: string;
}
const TG_TAGS = new Set([
'b',
'strong',
'i',
'em',
'u',
'ins',
's',
'strike',
'del',
'code',
'pre',
'a',
'tg-spoiler',
'span',
]);
function tokenize(input: string): Token[] {
const tokens: Token[] = [];
const pattern = /<(\/?)([a-z][a-z0-9-]*)(\s+[^>]*)?>|
/gi;
const matches = [...input.matchAll(pattern)];
let lastIdx = 0;
for (const m of matches) {
const idx = m.index || 0;
if (idx > lastIdx) tokens.push({ kind: 'text', value: input.slice(lastIdx, idx) });
if (m[0].toLowerCase().startsWith('
(i === 0 ? [p] : [
, p]));
}
type Frame = { tag: string | null; href?: string; children: ReactNode[] };
function wrap(frame: Frame, key: number): ReactNode {
const k = `el-${key}`;
switch (frame.tag) {
case 'b':
case 'strong':
return {frame.children};
case 'i':
case 'em':
return {frame.children};
case 'u':
case 'ins':
return {frame.children};
case 's':
case 'strike':
case 'del':
return {frame.children};
case 'code':
return (
{frame.children}
);
case 'pre':
return (
{frame.children}
);
case 'a': {
const safeHref =
frame.href && /^(https?:|tg:|mailto:|tel:)/i.test(frame.href) ? frame.href : '#';
return (
{frame.children}
);
}
case 'tg-spoiler':
case 'span':
return {frame.children};
default:
return {frame.children};
}
}
function tokensToReact(tokens: Token[]): ReactNode {
const root: Frame = { tag: null, children: [] };
const stack: Frame[] = [root];
let textKey = 0;
let elKey = 0;
for (const tok of tokens) {
const top = stack[stack.length - 1];
if (tok.kind === 'text') {
top.children.push({renderText(tok.value || '', textKey)});
} else if (tok.kind === 'br') {
top.children.push(${t('admin.broadcasts.previewEmpty', '— пусто —')}
`; return createPortal(