nakama/frontend/src/pages/Profile.jsx

313 lines
11 KiB
React
Raw Normal View History

2025-11-03 20:35:01 +00:00
import { useState } from 'react'
import { Settings, Heart, Edit2, Star, Shield } from 'lucide-react'
import { updateProfile } from '../utils/api'
import { hapticFeedback, openTelegramLink } from '../utils/telegram'
import ThemeToggle from '../components/ThemeToggle'
import './Profile.css'
export default function Profile({ user, setUser }) {
const [showSettings, setShowSettings] = useState(false)
const [showEditBio, setShowEditBio] = useState(false)
const [bio, setBio] = useState(user.bio || '')
const [settings, setSettings] = useState(user.settings || {
whitelist: {
noFurry: false,
onlyAnime: false,
noNSFW: true
},
searchPreference: 'mixed'
})
const [saving, setSaving] = useState(false)
const handleSaveBio = async () => {
try {
setSaving(true)
hapticFeedback('light')
const updatedUser = await updateProfile({ bio })
setUser({ ...user, bio })
setShowEditBio(false)
hapticFeedback('success')
} catch (error) {
console.error('Ошибка сохранения:', error)
hapticFeedback('error')
} finally {
setSaving(false)
}
}
const handleSaveSettings = async () => {
try {
setSaving(true)
hapticFeedback('light')
const updatedUser = await updateProfile({ settings })
setUser({ ...user, settings })
setShowSettings(false)
hapticFeedback('success')
} catch (error) {
console.error('Ошибка сохранения:', error)
hapticFeedback('error')
} finally {
setSaving(false)
}
}
const handleDonate = () => {
hapticFeedback('light')
// В будущем здесь будет интеграция Telegram Stars
openTelegramLink('https://t.me/donate')
}
const updateWhitelistSetting = async (key, value) => {
const newSettings = {
...settings,
whitelist: {
...settings.whitelist,
[key]: value
}
}
setSettings(newSettings)
// Сохранить сразу на сервер
try {
await updateProfile({ settings: newSettings })
hapticFeedback('success')
} catch (error) {
console.error('Ошибка сохранения настроек:', error)
hapticFeedback('error')
}
}
const updateSearchPreference = (value) => {
setSettings({
...settings,
searchPreference: value
})
}
return (
<div className="profile-page">
{/* Хедер */}
<div className="profile-header">
<h1>Профиль</h1>
<button className="settings-btn" onClick={() => setShowSettings(true)}>
<Settings size={24} />
</button>
</div>
{/* Информация о пользователе */}
<div className="profile-info card">
<img
src={user.photoUrl || '/default-avatar.png'}
alt={user.username}
className="profile-avatar"
/>
<div className="profile-details">
<h2 className="profile-name">
{user.firstName} {user.lastName}
{(user.role === 'moderator' || user.role === 'admin') && (
<Shield size={20} color="var(--button-accent)" />
)}
</h2>
<p className="profile-username">@{user.username}</p>
{user.bio ? (
<div className="profile-bio">
<p>{user.bio}</p>
<button className="edit-bio-btn" onClick={() => setShowEditBio(true)}>
<Edit2 size={16} />
</button>
</div>
) : (
<button className="add-bio-btn" onClick={() => setShowEditBio(true)}>
<Edit2 size={16} />
<span>Добавить описание</span>
</button>
)}
</div>
<div className="profile-stats">
<div className="stat-item">
<span className="stat-value">{user.followersCount || 0}</span>
<span className="stat-label">Подписчики</span>
</div>
<div className="stat-divider" />
<div className="stat-item">
<span className="stat-value">{user.followingCount || 0}</span>
<span className="stat-label">Подписки</span>
</div>
</div>
</div>
{/* Быстрые настройки */}
<div className="quick-settings">
<h3>Быстрые настройки</h3>
<div className="setting-item card">
<div>
<div className="setting-name">Тема оформления</div>
<div className="setting-desc">Светлая / Тёмная / Авто</div>
</div>
<ThemeToggle showLabel />
</div>
<div className="setting-item card">
<div>
<div className="setting-name">Скрыть контент 18+</div>
<div className="setting-desc">Не показывать посты с пометкой NSFW</div>
</div>
<label className="toggle">
<input
type="checkbox"
checked={settings.whitelist.noNSFW}
onChange={(e) => updateWhitelistSetting('noNSFW', e.target.checked)}
/>
<span className="toggle-slider" />
</label>
</div>
</div>
{/* Модальное окно редактирования bio */}
{showEditBio && (
<div className="modal-overlay" onClick={() => setShowEditBio(false)}>
<div className="modal-content" onClick={e => e.stopPropagation()}>
<div className="modal-header">
<h2>Описание профиля</h2>
<button
className="submit-btn"
onClick={handleSaveBio}
disabled={saving}
>
{saving ? 'Сохранение...' : 'Сохранить'}
</button>
</div>
<div className="modal-body">
<textarea
placeholder="Расскажите о себе..."
value={bio}
onChange={e => setBio(e.target.value)}
maxLength={300}
rows={6}
autoFocus
/>
<div className="char-count">
{bio.length} / 300
</div>
</div>
</div>
</div>
)}
{/* Модальное окно настроек */}
{showSettings && (
<div className="modal-overlay" onClick={() => setShowSettings(false)}>
<div className="modal-content settings-modal" onClick={e => e.stopPropagation()}>
<div className="modal-header">
<h2>Настройки</h2>
<button
className="submit-btn"
onClick={handleSaveSettings}
disabled={saving}
>
{saving ? 'Сохранение...' : 'Сохранить'}
</button>
</div>
<div className="modal-body">
<div className="settings-section">
<h3>Фильтры контента</h3>
<div className="setting-row">
<div>
<div className="setting-name">Без Furry</div>
<div className="setting-desc">Скрыть посты с тегом Furry</div>
</div>
<label className="toggle">
<input
type="checkbox"
checked={settings.whitelist.noFurry}
onChange={(e) => updateWhitelistSetting('noFurry', e.target.checked)}
/>
<span className="toggle-slider" />
</label>
</div>
<div className="setting-row">
<div>
<div className="setting-name">Только Anime</div>
<div className="setting-desc">Показывать только Anime</div>
</div>
<label className="toggle">
<input
type="checkbox"
checked={settings.whitelist.onlyAnime}
onChange={(e) => updateWhitelistSetting('onlyAnime', e.target.checked)}
/>
<span className="toggle-slider" />
</label>
</div>
<div className="setting-row">
<div>
<div className="setting-name">Без NSFW</div>
<div className="setting-desc">Скрыть контент 18+</div>
</div>
<label className="toggle">
<input
type="checkbox"
checked={settings.whitelist.noNSFW}
onChange={(e) => updateWhitelistSetting('noNSFW', e.target.checked)}
/>
<span className="toggle-slider" />
</label>
</div>
</div>
<div className="settings-section">
<h3>Настройки поиска</h3>
<div className="radio-group">
<label className="radio-item">
<input
type="radio"
name="searchPref"
checked={settings.searchPreference === 'furry'}
onChange={() => updateSearchPreference('furry')}
/>
<span>Только Furry (e621)</span>
</label>
<label className="radio-item">
<input
type="radio"
name="searchPref"
checked={settings.searchPreference === 'anime'}
onChange={() => updateSearchPreference('anime')}
/>
<span>Только Anime (gelbooru)</span>
</label>
<label className="radio-item">
<input
type="radio"
name="searchPref"
checked={settings.searchPreference === 'mixed'}
onChange={() => updateSearchPreference('mixed')}
/>
<span>Смешанный поиск</span>
</label>
</div>
</div>
</div>
</div>
</div>
)}
</div>
)
}