Update files
This commit is contained in:
parent
fbeb53d96f
commit
97f20486a1
|
|
@ -187,6 +187,63 @@ router.get('/posts', authenticateModeration, requireModerationAccess, async (req
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Получить пост с комментариями
|
||||||
|
router.get('/posts/:id', authenticateModeration, requireModerationAccess, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const post = await Post.findById(req.params.id)
|
||||||
|
.populate('author', 'username firstName lastName photoUrl')
|
||||||
|
.populate('comments.author', 'username firstName lastName photoUrl')
|
||||||
|
.exec();
|
||||||
|
|
||||||
|
if (!post) {
|
||||||
|
return res.status(404).json({ error: 'Пост не найден' });
|
||||||
|
}
|
||||||
|
|
||||||
|
res.json({
|
||||||
|
post: {
|
||||||
|
id: post._id,
|
||||||
|
author: post.author ? serializeUser(post.author) : null,
|
||||||
|
content: post.content,
|
||||||
|
hashtags: post.hashtags,
|
||||||
|
tags: post.tags,
|
||||||
|
images: post.images || (post.imageUrl ? [post.imageUrl] : []),
|
||||||
|
comments: post.comments || [],
|
||||||
|
likesCount: post.likes?.length || 0,
|
||||||
|
isNSFW: post.isNSFW,
|
||||||
|
createdAt: post.createdAt
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка получения поста:', error);
|
||||||
|
res.status(500).json({ error: 'Ошибка сервера' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Удалить комментарий (модераторский интерфейс)
|
||||||
|
router.delete('/posts/:postId/comments/:commentId', authenticateModeration, requireModerationAccess, async (req, res) => {
|
||||||
|
try {
|
||||||
|
const post = await Post.findById(req.params.postId);
|
||||||
|
|
||||||
|
if (!post) {
|
||||||
|
return res.status(404).json({ error: 'Пост не найден' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const comment = post.comments.id(req.params.commentId);
|
||||||
|
if (!comment) {
|
||||||
|
return res.status(404).json({ error: 'Комментарий не найден' });
|
||||||
|
}
|
||||||
|
|
||||||
|
post.comments.pull(req.params.commentId);
|
||||||
|
await post.save();
|
||||||
|
await post.populate('comments.author', 'username firstName lastName photoUrl');
|
||||||
|
|
||||||
|
res.json({ comments: post.comments });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка удаления комментария:', error);
|
||||||
|
res.status(500).json({ error: 'Ошибка сервера' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
router.put('/posts/:id', authenticateModeration, requireModerationAccess, async (req, res) => {
|
router.put('/posts/:id', authenticateModeration, requireModerationAccess, async (req, res) => {
|
||||||
const { content, hashtags, tags, isNSFW } = req.body;
|
const { content, hashtags, tags, isNSFW } = req.body;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
import { Settings, Heart, Edit2, Shield, Copy, Users } from 'lucide-react'
|
import { Settings, Heart, Edit2, Shield } from 'lucide-react'
|
||||||
import { updateProfile } from '../utils/api'
|
import { updateProfile } from '../utils/api'
|
||||||
import { hapticFeedback, showAlert } from '../utils/telegram'
|
import { hapticFeedback } from '../utils/telegram'
|
||||||
import { decodeHtmlEntities } from '../utils/htmlEntities'
|
|
||||||
import ThemeToggle from '../components/ThemeToggle'
|
import ThemeToggle from '../components/ThemeToggle'
|
||||||
import FollowListModal from '../components/FollowListModal'
|
|
||||||
import './Profile.css'
|
import './Profile.css'
|
||||||
|
|
||||||
const DONATION_URL = 'https://donatepay.ru/don/1435720'
|
const DONATION_URL = 'https://donatepay.ru/don/1435720'
|
||||||
|
|
@ -40,8 +38,6 @@ export default function Profile({ user, setUser }) {
|
||||||
const [showSettings, setShowSettings] = useState(false)
|
const [showSettings, setShowSettings] = useState(false)
|
||||||
const [showEditBio, setShowEditBio] = useState(false)
|
const [showEditBio, setShowEditBio] = useState(false)
|
||||||
const [bio, setBio] = useState(user.bio || '')
|
const [bio, setBio] = useState(user.bio || '')
|
||||||
const [showFollowers, setShowFollowers] = useState(false)
|
|
||||||
const [showFollowing, setShowFollowing] = useState(false)
|
|
||||||
const [settings, setSettings] = useState(normalizeSettings(user.settings))
|
const [settings, setSettings] = useState(normalizeSettings(user.settings))
|
||||||
const [saving, setSaving] = useState(false)
|
const [saving, setSaving] = useState(false)
|
||||||
|
|
||||||
|
|
@ -144,7 +140,7 @@ export default function Profile({ user, setUser }) {
|
||||||
|
|
||||||
{user.bio ? (
|
{user.bio ? (
|
||||||
<div className="profile-bio">
|
<div className="profile-bio">
|
||||||
<p>{decodeHtmlEntities(user.bio)}</p>
|
<p>{user.bio}</p>
|
||||||
<button className="edit-bio-btn" onClick={() => setShowEditBio(true)}>
|
<button className="edit-bio-btn" onClick={() => setShowEditBio(true)}>
|
||||||
<Edit2 size={16} />
|
<Edit2 size={16} />
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -158,12 +154,12 @@ export default function Profile({ user, setUser }) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="profile-stats">
|
<div className="profile-stats">
|
||||||
<div className="stat-item" onClick={() => setShowFollowers(true)} style={{ cursor: 'pointer' }}>
|
<div className="stat-item">
|
||||||
<span className="stat-value">{user.followersCount || 0}</span>
|
<span className="stat-value">{user.followersCount || 0}</span>
|
||||||
<span className="stat-label">Подписчики</span>
|
<span className="stat-label">Подписчики</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="stat-divider" />
|
<div className="stat-divider" />
|
||||||
<div className="stat-item" onClick={() => setShowFollowing(true)} style={{ cursor: 'pointer' }}>
|
<div className="stat-item">
|
||||||
<span className="stat-value">{user.followingCount || 0}</span>
|
<span className="stat-value">{user.followingCount || 0}</span>
|
||||||
<span className="stat-label">Подписки</span>
|
<span className="stat-label">Подписки</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -185,62 +181,6 @@ export default function Profile({ user, setUser }) {
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Реферальная ссылка */}
|
|
||||||
{user.referralCode && (
|
|
||||||
<div className="referral-card card">
|
|
||||||
<div className="referral-content">
|
|
||||||
<div className="referral-icon">
|
|
||||||
<Users size={20} />
|
|
||||||
</div>
|
|
||||||
<div className="referral-text">
|
|
||||||
<h3>Пригласи друзей</h3>
|
|
||||||
<p>Получи +1 к счетчику, когда приглашенный создаст первый пост</p>
|
|
||||||
<div className="referral-stats">
|
|
||||||
Приглашено: <strong>{user.referralsCount || 0}</strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="referral-link-section">
|
|
||||||
<div className="referral-link">
|
|
||||||
<code>{`https://t.me/${import.meta.env.VITE_TELEGRAM_BOT_NAME || 'NakamaSpaceBot'}?startapp=${user.referralCode}`}</code>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
className="referral-copy-btn"
|
|
||||||
onClick={async () => {
|
|
||||||
try {
|
|
||||||
hapticFeedback('light')
|
|
||||||
const botName = import.meta.env.VITE_TELEGRAM_BOT_NAME || 'NakamaSpaceBot'
|
|
||||||
const referralLink = `https://t.me/${botName}?startapp=${user.referralCode}`
|
|
||||||
|
|
||||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
||||||
await navigator.clipboard.writeText(referralLink)
|
|
||||||
hapticFeedback('success')
|
|
||||||
showAlert('✅ Ссылка скопирована!')
|
|
||||||
} else {
|
|
||||||
const textArea = document.createElement('textarea')
|
|
||||||
textArea.value = referralLink
|
|
||||||
textArea.style.position = 'fixed'
|
|
||||||
textArea.style.opacity = '0'
|
|
||||||
document.body.appendChild(textArea)
|
|
||||||
textArea.select()
|
|
||||||
document.execCommand('copy')
|
|
||||||
document.body.removeChild(textArea)
|
|
||||||
hapticFeedback('success')
|
|
||||||
showAlert('✅ Ссылка скопирована!')
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Ошибка копирования:', error)
|
|
||||||
hapticFeedback('error')
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Copy size={18} />
|
|
||||||
<span>Копировать</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="profile-powered">
|
<div className="profile-powered">
|
||||||
Powered by glpshcn \\ RBach \\ E621 \\ GelBooru
|
Powered by glpshcn \\ RBach \\ E621 \\ GelBooru
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -394,26 +334,6 @@ export default function Profile({ user, setUser }) {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Модалка подписчиков */}
|
|
||||||
{showFollowers && user && (
|
|
||||||
<FollowListModal
|
|
||||||
users={user.followers || []}
|
|
||||||
title="Подписчики"
|
|
||||||
currentUser={user}
|
|
||||||
onClose={() => setShowFollowers(false)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Модалка подписок */}
|
|
||||||
{showFollowing && user && (
|
|
||||||
<FollowListModal
|
|
||||||
users={user.following || []}
|
|
||||||
title="Подписки"
|
|
||||||
currentUser={user}
|
|
||||||
onClose={() => setShowFollowing(false)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,10 +105,10 @@ export const confirmRemoveAdmin = (adminId, code) =>
|
||||||
api.post('/mod-app/admins/confirm-remove', { adminId, code }).then((res) => res.data)
|
api.post('/mod-app/admins/confirm-remove', { adminId, code }).then((res) => res.data)
|
||||||
|
|
||||||
export const getPostComments = (postId) =>
|
export const getPostComments = (postId) =>
|
||||||
api.get(`/posts/${postId}`).then((res) => res.data.post)
|
api.get(`/mod-app/posts/${postId}`).then((res) => res.data.post)
|
||||||
|
|
||||||
export const deleteComment = (postId, commentId) =>
|
export const deleteComment = (postId, commentId) =>
|
||||||
api.delete(`/posts/${postId}/comments/${commentId}`).then((res) => res.data)
|
api.delete(`/mod-app/posts/${postId}/comments/${commentId}`).then((res) => res.data)
|
||||||
|
|
||||||
export default api
|
export default api
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue