Update files
This commit is contained in:
parent
0943fd1d19
commit
10d909711d
|
|
@ -29,6 +29,7 @@ export default function PostCard({ post, currentUser, onUpdate }) {
|
|||
const [showFullView, setShowFullView] = useState(false)
|
||||
const [showComments, setShowComments] = useState(false)
|
||||
const [showMenu, setShowMenu] = useState(false)
|
||||
const [menuButtonPosition, setMenuButtonPosition] = useState(null)
|
||||
|
||||
// Проверка на существование автора
|
||||
if (!post.author) {
|
||||
|
|
@ -168,8 +169,17 @@ export default function PostCard({ post, currentUser, onUpdate }) {
|
|||
|
||||
<button
|
||||
className="menu-btn"
|
||||
data-menu-button={post._id}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
const button = e.currentTarget
|
||||
const rect = button.getBoundingClientRect()
|
||||
setMenuButtonPosition({
|
||||
top: rect.top,
|
||||
left: rect.left,
|
||||
bottom: rect.bottom,
|
||||
right: rect.right
|
||||
})
|
||||
setShowMenu(true)
|
||||
}}
|
||||
>
|
||||
|
|
@ -346,11 +356,17 @@ export default function PostCard({ post, currentUser, onUpdate }) {
|
|||
<PostMenu
|
||||
post={post}
|
||||
currentUser={currentUser}
|
||||
onClose={() => setShowMenu(false)}
|
||||
onClose={() => {
|
||||
setShowMenu(false)
|
||||
setMenuButtonPosition(null)
|
||||
}}
|
||||
onDelete={async () => {
|
||||
await handleDelete()
|
||||
setShowMenu(false)
|
||||
setMenuButtonPosition(null)
|
||||
}}
|
||||
onUpdate={onUpdate}
|
||||
buttonPosition={menuButtonPosition}
|
||||
/>,
|
||||
document.body
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,11 @@
|
|||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: var(--bg-secondary);
|
||||
background: transparent;
|
||||
z-index: 10500;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 16px;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
overflow: hidden;
|
||||
touch-action: none;
|
||||
overscroll-behavior: contain;
|
||||
|
|
@ -29,75 +29,149 @@
|
|||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
.menu-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
.menu-content {
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
padding: 4px;
|
||||
margin-top: 8px;
|
||||
min-width: 140px;
|
||||
z-index: 10501;
|
||||
}
|
||||
|
||||
.menu-header h2 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
.menu-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
width: 100%;
|
||||
padding: 10px 12px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
color: var(--text-primary);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.menu-item:hover {
|
||||
background: var(--bg-primary);
|
||||
}
|
||||
|
||||
.menu-item:active {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.menu-close-btn {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
background: var(--bg-primary);
|
||||
background: transparent;
|
||||
color: var(--text-primary);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
/* Убираем pointer-events чтобы клики работали */
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.menu-close-btn svg {
|
||||
stroke: currentColor;
|
||||
}
|
||||
|
||||
.menu-items {
|
||||
padding-top: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
/* Убираем pointer-events и touch-action чтобы клики работали */
|
||||
}
|
||||
|
||||
.menu-item {
|
||||
width: 100%;
|
||||
padding: 16px;
|
||||
background: var(--bg-primary);
|
||||
border: none;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
color: var(--text-primary);
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
border-radius: 12px;
|
||||
cursor: pointer;
|
||||
/* Убираем pointer-events и touch-action чтобы клики работали */
|
||||
}
|
||||
|
||||
.menu-item svg {
|
||||
stroke: currentColor;
|
||||
}
|
||||
|
||||
.menu-item:active {
|
||||
opacity: 0.7;
|
||||
transform: scale(0.98);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.menu-item.danger {
|
||||
color: #FF3B30;
|
||||
}
|
||||
|
||||
.menu-item.danger svg {
|
||||
stroke: #FF3B30;
|
||||
}
|
||||
|
||||
/* Edit modal styles */
|
||||
.edit-menu-content {
|
||||
background: var(--bg-secondary);
|
||||
border-radius: 16px;
|
||||
padding: 16px;
|
||||
max-width: 90vw;
|
||||
width: 400px;
|
||||
max-height: 80vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.edit-menu-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.edit-menu-header h3 {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: var(--text-primary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.edit-content-input {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--divider-color);
|
||||
border-radius: 12px;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
font-size: 15px;
|
||||
line-height: 1.5;
|
||||
resize: none;
|
||||
font-family: inherit;
|
||||
}
|
||||
|
||||
.edit-menu-actions {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.edit-cancel-btn {
|
||||
padding: 10px 16px;
|
||||
border-radius: 8px;
|
||||
background: var(--bg-primary);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--divider-color);
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.edit-save-btn {
|
||||
padding: 10px 16px;
|
||||
border-radius: 8px;
|
||||
background: var(--button-accent);
|
||||
color: white;
|
||||
border: none;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.edit-save-btn:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.report-modal-header {
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid var(--divider-color);
|
||||
|
|
|
|||
|
|
@ -1,13 +1,15 @@
|
|||
import { X, Trash2, AlertCircle, Flag } from 'lucide-react'
|
||||
import { X, Trash2, AlertCircle, Flag, Edit2 } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
import { createPortal } from 'react-dom'
|
||||
import { reportPost } from '../utils/api'
|
||||
import { reportPost, editPost } from '../utils/api'
|
||||
import { hapticFeedback, showConfirm } from '../utils/telegram'
|
||||
import './PostMenu.css'
|
||||
|
||||
export default function PostMenu({ post, currentUser, onClose, onDelete }) {
|
||||
export default function PostMenu({ post, currentUser, onClose, onDelete, onUpdate, buttonPosition }) {
|
||||
const [showReportModal, setShowReportModal] = useState(false)
|
||||
const [showEditModal, setShowEditModal] = useState(false)
|
||||
const [reportReason, setReportReason] = useState('')
|
||||
const [editContent, setEditContent] = useState(post.content || '')
|
||||
const [submitting, setSubmitting] = useState(false)
|
||||
|
||||
const isOwnPost = post.author._id === currentUser.id
|
||||
|
|
@ -77,6 +79,31 @@ export default function PostMenu({ post, currentUser, onClose, onDelete }) {
|
|||
)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
const handleOverlayClick = (e) => {
|
||||
// Закрывать только при клике на overlay, не на контент
|
||||
if (e.target === e.currentTarget) {
|
||||
|
|
@ -84,6 +111,63 @@ export default function PostMenu({ post, currentUser, onClose, onDelete }) {
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
)
|
||||
}
|
||||
|
||||
const menuStyle = buttonPosition ? {
|
||||
position: 'fixed',
|
||||
top: `${buttonPosition.bottom + 4}px`,
|
||||
left: `${buttonPosition.left + (buttonPosition.right - buttonPosition.left) / 2}px`,
|
||||
transform: 'translateX(-50%)',
|
||||
width: 'auto',
|
||||
minWidth: '140px'
|
||||
} : {}
|
||||
|
||||
return createPortal(
|
||||
<div
|
||||
className="post-menu-overlay"
|
||||
|
|
@ -91,31 +175,26 @@ export default function PostMenu({ post, currentUser, onClose, onDelete }) {
|
|||
onTouchStart={(e) => e.stopPropagation()}
|
||||
onClick={handleOverlayClick}
|
||||
>
|
||||
<div className="menu-header" onClick={(e) => e.stopPropagation()}>
|
||||
<button className="menu-close-btn" onClick={onClose}>
|
||||
<X size={24} />
|
||||
</button>
|
||||
<h2>Действия</h2>
|
||||
<div style={{ width: 40 }} />
|
||||
</div>
|
||||
|
||||
<div className="menu-items" onClick={(e) => e.stopPropagation()}>
|
||||
{isOwnPost || isModerator ? (
|
||||
<button className="menu-item danger" onClick={onDelete}>
|
||||
<Trash2 size={20} />
|
||||
<span>Удалить пост</span>
|
||||
</button>
|
||||
) : (
|
||||
<button className="menu-item" onClick={() => setShowReportModal(true)}>
|
||||
<Flag size={20} />
|
||||
<span>Пожаловаться</span>
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button className="menu-item" onClick={onClose}>
|
||||
<X size={20} />
|
||||
<span>Отмена</span>
|
||||
</button>
|
||||
<div className="menu-content" style={menuStyle} onClick={(e) => e.stopPropagation()}>
|
||||
<div className="menu-items" onClick={(e) => e.stopPropagation()}>
|
||||
{isOwnPost || isModerator ? (
|
||||
<>
|
||||
<button className="menu-item" onClick={() => setShowEditModal(true)}>
|
||||
<Edit2 size={18} />
|
||||
<span>Редактировать</span>
|
||||
</button>
|
||||
<button className="menu-item danger" onClick={onDelete}>
|
||||
<Trash2 size={18} />
|
||||
<span>Удалить</span>
|
||||
</button>
|
||||
</>
|
||||
) : (
|
||||
<button className="menu-item" onClick={() => setShowReportModal(true)}>
|
||||
<Flag size={18} />
|
||||
<span>Пожаловаться</span>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>,
|
||||
document.body
|
||||
|
|
|
|||
Loading…
Reference in New Issue