2025-12-04 22:28:51 +00:00
|
|
|
import { X, Trash2, AlertCircle, Flag, Edit2 } from 'lucide-react'
|
2025-11-03 20:35:01 +00:00
|
|
|
import { useState } from 'react'
|
2025-12-04 20:27:45 +00:00
|
|
|
import { createPortal } from 'react-dom'
|
2025-12-04 22:28:51 +00:00
|
|
|
import { reportPost, editPost } from '../utils/api'
|
2025-11-03 20:35:01 +00:00
|
|
|
import { hapticFeedback, showConfirm } from '../utils/telegram'
|
|
|
|
|
import './PostMenu.css'
|
|
|
|
|
|
2025-12-04 22:28:51 +00:00
|
|
|
export default function PostMenu({ post, currentUser, onClose, onDelete, onUpdate, buttonPosition }) {
|
2025-11-03 20:35:01 +00:00
|
|
|
const [showReportModal, setShowReportModal] = useState(false)
|
2025-12-04 22:28:51 +00:00
|
|
|
const [showEditModal, setShowEditModal] = useState(false)
|
2025-11-03 20:35:01 +00:00
|
|
|
const [reportReason, setReportReason] = useState('')
|
2025-12-04 22:28:51 +00:00
|
|
|
const [editContent, setEditContent] = useState(post.content || '')
|
2025-11-03 20:35:01 +00:00
|
|
|
const [submitting, setSubmitting] = useState(false)
|
|
|
|
|
|
|
|
|
|
const isOwnPost = post.author._id === currentUser.id
|
|
|
|
|
const isModerator = currentUser.role === 'moderator' || currentUser.role === 'admin'
|
|
|
|
|
|
|
|
|
|
const handleReport = async () => {
|
|
|
|
|
if (!reportReason.trim()) {
|
|
|
|
|
alert('Укажите причину жалобы')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
setSubmitting(true)
|
|
|
|
|
hapticFeedback('light')
|
|
|
|
|
await reportPost(post._id, reportReason)
|
|
|
|
|
hapticFeedback('success')
|
|
|
|
|
alert('Жалоба отправлена')
|
|
|
|
|
onClose()
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Ошибка отправки жалобы:', error)
|
|
|
|
|
hapticFeedback('error')
|
|
|
|
|
alert('Ошибка отправки жалобы')
|
|
|
|
|
} finally {
|
|
|
|
|
setSubmitting(false)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (showReportModal) {
|
2025-11-03 22:41:34 +00:00
|
|
|
const handleReportOverlayClick = (e) => {
|
|
|
|
|
if (e.target === e.currentTarget) {
|
|
|
|
|
setShowReportModal(false)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-04 20:27:45 +00:00
|
|
|
return createPortal(
|
|
|
|
|
<div
|
|
|
|
|
className="report-modal-overlay"
|
|
|
|
|
onMouseDown={(e) => e.stopPropagation()}
|
|
|
|
|
onTouchStart={(e) => e.stopPropagation()}
|
|
|
|
|
onClick={handleReportOverlayClick}
|
|
|
|
|
>
|
2025-11-03 22:41:34 +00:00
|
|
|
<div className="report-modal-header" onClick={(e) => e.stopPropagation()}>
|
2025-11-03 21:57:35 +00:00
|
|
|
<button className="menu-close-btn" onClick={onClose}>
|
|
|
|
|
<X size={24} />
|
|
|
|
|
</button>
|
|
|
|
|
<h2>Пожаловаться</h2>
|
|
|
|
|
<button
|
|
|
|
|
className="submit-btn"
|
|
|
|
|
onClick={handleReport}
|
|
|
|
|
disabled={submitting || !reportReason.trim()}
|
|
|
|
|
>
|
|
|
|
|
{submitting ? 'Отправка...' : 'Отправить'}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
2025-11-03 20:35:01 +00:00
|
|
|
|
2025-11-03 22:41:34 +00:00
|
|
|
<div className="report-modal-body" onClick={(e) => e.stopPropagation()}>
|
2025-11-03 21:57:35 +00:00
|
|
|
<textarea
|
|
|
|
|
placeholder="Опишите причину жалобы..."
|
|
|
|
|
value={reportReason}
|
|
|
|
|
onChange={e => setReportReason(e.target.value)}
|
|
|
|
|
maxLength={500}
|
|
|
|
|
autoFocus
|
|
|
|
|
/>
|
2025-11-03 20:35:01 +00:00
|
|
|
</div>
|
2025-12-04 20:27:45 +00:00
|
|
|
</div>,
|
|
|
|
|
document.body
|
2025-11-03 20:35:01 +00:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-04 22:28:51 +00:00
|
|
|
const handleEdit = async () => {
|
|
|
|
|
if (!editContent.trim()) {
|
|
|
|
|
alert('Текст поста не может быть пустым')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
setSubmitting(true)
|
|
|
|
|
hapticFeedback('light')
|
|
|
|
|
await editPost(post._id, { content: editContent })
|
|
|
|
|
hapticFeedback('success')
|
|
|
|
|
setShowEditModal(false)
|
|
|
|
|
onClose()
|
|
|
|
|
if (onUpdate) {
|
|
|
|
|
onUpdate()
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Ошибка редактирования поста:', error)
|
|
|
|
|
hapticFeedback('error')
|
|
|
|
|
alert('Ошибка редактирования поста')
|
|
|
|
|
} finally {
|
|
|
|
|
setSubmitting(false)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-03 22:41:34 +00:00
|
|
|
const handleOverlayClick = (e) => {
|
|
|
|
|
// Закрывать только при клике на overlay, не на контент
|
|
|
|
|
if (e.target === e.currentTarget) {
|
|
|
|
|
onClose()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-04 22:28:51 +00:00
|
|
|
if (showEditModal) {
|
|
|
|
|
return createPortal(
|
|
|
|
|
<div
|
|
|
|
|
className="post-menu-overlay"
|
|
|
|
|
onMouseDown={(e) => e.stopPropagation()}
|
|
|
|
|
onTouchStart={(e) => e.stopPropagation()}
|
|
|
|
|
onClick={handleOverlayClick}
|
|
|
|
|
>
|
|
|
|
|
<div className="edit-menu-content" onClick={(e) => e.stopPropagation()}>
|
|
|
|
|
<div className="edit-menu-header">
|
|
|
|
|
<h3>Редактировать пост</h3>
|
|
|
|
|
<button className="menu-close-btn" onClick={() => setShowEditModal(false)}>
|
|
|
|
|
<X size={18} />
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
<textarea
|
|
|
|
|
className="edit-content-input"
|
|
|
|
|
value={editContent}
|
|
|
|
|
onChange={e => setEditContent(e.target.value)}
|
|
|
|
|
placeholder="Текст поста..."
|
|
|
|
|
maxLength={2000}
|
|
|
|
|
rows={6}
|
|
|
|
|
autoFocus
|
|
|
|
|
/>
|
|
|
|
|
<div className="edit-menu-actions">
|
|
|
|
|
<button
|
|
|
|
|
className="edit-cancel-btn"
|
|
|
|
|
onClick={() => {
|
|
|
|
|
setShowEditModal(false)
|
|
|
|
|
setEditContent(post.content || '')
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Отмена
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
className="edit-save-btn"
|
|
|
|
|
onClick={handleEdit}
|
|
|
|
|
disabled={submitting || !editContent.trim()}
|
|
|
|
|
>
|
|
|
|
|
{submitting ? 'Сохранение...' : 'Сохранить'}
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>,
|
|
|
|
|
document.body
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-04 22:52:34 +00:00
|
|
|
const getMenuStyle = () => {
|
|
|
|
|
if (!buttonPosition) return {}
|
|
|
|
|
|
|
|
|
|
const menuWidth = 160 // Примерная ширина меню
|
|
|
|
|
const padding = 8 // Отступ от края экрана
|
|
|
|
|
const buttonCenterX = buttonPosition.left + (buttonPosition.right - buttonPosition.left) / 2
|
|
|
|
|
const windowWidth = window.innerWidth
|
|
|
|
|
|
|
|
|
|
let left = buttonCenterX
|
|
|
|
|
let transform = 'translateX(-50%)'
|
|
|
|
|
|
|
|
|
|
// Проверка правого края
|
|
|
|
|
if (buttonCenterX + menuWidth / 2 > windowWidth - padding) {
|
|
|
|
|
left = windowWidth - padding - menuWidth / 2
|
|
|
|
|
transform = 'translateX(-50%)'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Проверка левого края
|
|
|
|
|
if (buttonCenterX - menuWidth / 2 < padding) {
|
|
|
|
|
left = padding + menuWidth / 2
|
|
|
|
|
transform = 'translateX(-50%)'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
position: 'fixed',
|
|
|
|
|
top: `${buttonPosition.bottom + 4}px`,
|
|
|
|
|
left: `${left}px`,
|
|
|
|
|
transform: transform,
|
|
|
|
|
width: 'auto',
|
|
|
|
|
minWidth: '140px',
|
|
|
|
|
maxWidth: `${windowWidth - padding * 2}px`
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-12-04 22:28:51 +00:00
|
|
|
|
2025-12-04 20:27:45 +00:00
|
|
|
return createPortal(
|
2025-12-04 20:11:28 +00:00
|
|
|
<div
|
|
|
|
|
className="post-menu-overlay"
|
|
|
|
|
onMouseDown={(e) => e.stopPropagation()}
|
|
|
|
|
onTouchStart={(e) => e.stopPropagation()}
|
|
|
|
|
onClick={handleOverlayClick}
|
|
|
|
|
>
|
2025-12-04 22:52:34 +00:00
|
|
|
<div className="menu-content" style={getMenuStyle()} onClick={(e) => e.stopPropagation()}>
|
2025-12-04 22:28:51 +00:00
|
|
|
<div className="menu-items" onClick={(e) => e.stopPropagation()}>
|
2025-12-04 22:52:34 +00:00
|
|
|
{(isOwnPost || isModerator) && (
|
2025-12-04 22:28:51 +00:00
|
|
|
<>
|
2025-12-04 22:52:34 +00:00
|
|
|
{isOwnPost && (
|
|
|
|
|
<button className="menu-item" onClick={() => setShowEditModal(true)}>
|
|
|
|
|
<Edit2 size={18} />
|
|
|
|
|
<span>Редактировать</span>
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
2025-12-04 22:28:51 +00:00
|
|
|
<button className="menu-item danger" onClick={onDelete}>
|
|
|
|
|
<Trash2 size={18} />
|
|
|
|
|
<span>Удалить</span>
|
|
|
|
|
</button>
|
|
|
|
|
</>
|
2025-12-04 22:52:34 +00:00
|
|
|
)}
|
|
|
|
|
{!isOwnPost && (
|
2025-12-04 22:28:51 +00:00
|
|
|
<button className="menu-item" onClick={() => setShowReportModal(true)}>
|
|
|
|
|
<Flag size={18} />
|
|
|
|
|
<span>Пожаловаться</span>
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2025-11-03 20:35:01 +00:00
|
|
|
</div>
|
2025-12-04 20:27:45 +00:00
|
|
|
</div>,
|
|
|
|
|
document.body
|
2025-11-03 20:35:01 +00:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|