From 779f8f779d372c9c7f1911d1b0eadcb61d5ca0df Mon Sep 17 00:00:00 2001 From: DIKER0K Date: Sat, 20 Dec 2025 21:12:52 +0500 Subject: [PATCH] add promo to News --- src/renderer/pages/News.tsx | 145 +++++++++++++++++++++++++++++++----- 1 file changed, 127 insertions(+), 18 deletions(-) diff --git a/src/renderer/pages/News.tsx b/src/renderer/pages/News.tsx index c3a63f5..e0f067b 100644 --- a/src/renderer/pages/News.tsx +++ b/src/renderer/pages/News.tsx @@ -14,6 +14,14 @@ import { fetchNews, NewsItem, createNews, fetchMe, deleteNews } from '../api'; import { FullScreenLoader } from '../components/FullScreenLoader'; import { MarkdownEditor } from '../components/MarkdownEditor'; import DeleteOutlineIcon from '@mui/icons-material/DeleteOutline'; +import React from 'react'; +import CustomNotification, { + NotificationPosition, +} from '../components/Notifications/CustomNotification'; +import { + getNotifPositionFromSettings, + isNotificationsEnabled, +} from '../utils/notifications'; export const News = () => { const [news, setNews] = useState([]); @@ -31,6 +39,17 @@ export const News = () => { const [preview, setPreview] = useState(''); const [markdown, setMarkdown] = useState(''); + const [notifOpen, setNotifOpen] = useState(false); + const [notifMsg, setNotifMsg] = useState(''); + const [notifSeverity, setNotifSeverity] = useState< + 'success' | 'info' | 'warning' | 'error' + >('info'); + + const [notifPos, setNotifPos] = useState({ + vertical: 'bottom', + horizontal: 'center', + }); + const [isAdmin, setIsAdmin] = useState(false); useEffect(() => { @@ -103,6 +122,18 @@ export const News = () => { } }; + const showNotification = ( + message: React.ReactNode, + severity: 'success' | 'info' | 'warning' | 'error' = 'info', + position: NotificationPosition = getNotifPositionFromSettings(), + ) => { + if (!isNotificationsEnabled()) return; + setNotifMsg(message); + setNotifSeverity(severity); + setNotifPos(position); + setNotifOpen(true); + }; + const handleDeleteNews = async (id: string) => { const confirmed = window.confirm('Точно удалить эту новость?'); if (!confirmed) return; @@ -148,6 +179,69 @@ export const News = () => { ); } + const PromoInline = ({ code }: { code: string }) => { + const handleCopy = async () => { + try { + await navigator.clipboard.writeText(code); + showNotification(`Промокод ${code} скопирован`, 'success'); + } catch { + // fallback для старых браузеров + const textarea = document.createElement('textarea'); + textarea.value = code; + textarea.style.position = 'fixed'; + textarea.style.opacity = '0'; + document.body.appendChild(textarea); + textarea.focus(); + textarea.select(); + document.execCommand('copy'); + document.body.removeChild(textarea); + showNotification(`Промокод ${code} скопирован`, 'success'); + } + }; + + return ( + + {code} + + ); + }; + + const renderWithPromoCodes = (text: string) => { + const parts = text.split(/(\/\/[A-Z0-9-_]+)/g); + + return parts.map((part, i) => { + if (part.startsWith('//')) { + const code = part.slice(2); + return ; + } + return part; + }); + }; + return ( { '& .MuiInputBase-root': { backgroundColor: 'rgba(0,0,0,0.6)', color: 'white', - borderRadius: '1.2vw' + borderRadius: '1.2vw', }, '& .MuiInputLabel-root': { color: 'rgba(255,255,255,0.7)', @@ -230,21 +324,24 @@ export const News = () => { backgroundColor: 'rgba(0,0,0,0.6)', borderRadius: '1.2vw', overflow: 'hidden', - border: 'none' + border: 'none', }, - '& .editor-toolbar': { // полоски(разделители) иконок + '& .editor-toolbar': { + // полоски(разделители) иконок background: 'transparent', color: 'white', border: 'none', - borderBottom: '1px solid #FFFFFF' + borderBottom: '1px solid #FFFFFF', }, - '& .editor-toolbar .fa': { // все иконки + '& .editor-toolbar .fa': { + // все иконки color: 'white', }, - '& .CodeMirror': { // поле ввода + '& .CodeMirror': { + // поле ввода backgroundColor: 'transparent', color: 'white', - border: 'none' + border: 'none', }, }} > @@ -283,7 +380,7 @@ export const News = () => { filter: 'brightness(1.05)', transform: 'scale(1.02)', }, - transition: 'all 0.5s ease' + transition: 'all 0.5s ease', }} > {creating ? 'Публикация...' : 'Опубликовать'} @@ -432,12 +529,10 @@ export const News = () => { onClick={() => handleToggleExpand(item.id)} sx={{ transform: isExpanded ? 'rotate(180deg)' : 'rotate(0deg)', - background: - 'rgba(242,113,33,0.15)', + background: 'rgba(242,113,33,0.15)', borderRadius: '1.4vw', '&:hover': { - background: - 'rgba(242,113,33,0.4)', + background: 'rgba(242,113,33,0.4)', }, transition: 'all 0.25s ease', }} @@ -505,7 +600,7 @@ export const News = () => { ( + p: ({ node, children, ...props }) => ( { fontSize: '1.5vw', lineHeight: 1.6, mb: 1, - whiteSpace: 'pre-line', // ← вот это + whiteSpace: 'pre-line', + textAlign: 'center', // вместо alignItems center '&:last-of-type': { mb: 0 }, - display: 'flex', - flexDirection: 'column', - alignItems: 'center', + wordBreak: 'break-word', + overflowWrap: 'anywhere', }} - /> + > + {React.Children.map(children, (child) => + typeof child === 'string' + ? renderWithPromoCodes(child) + : child, + )} + ), strong: ({ node, ...props }) => ( { ); })} + setNotifOpen(false)} + autoHideDuration={2500} + /> );