import { useEffect, useState } from "react"; import { useParams, Link, useNavigate } from "react-router-dom"; import { useAuth } from "../contexts/AuthContext"; import { getStory, deleteStory as apiDeleteStory, getSessionsList, getPlayerCharacters, } from "../services/api"; import type { Story, PlayerCharacter } from "../types"; import "./StoryDetailPage.css"; interface SessionsInfo { count: number; totalMessages: number; } export default function StoryDetailPage() { const { id } = useParams<{ id: string }>(); const navigate = useNavigate(); const { isAuthenticated } = useAuth(); const [story, setStory] = useState(null); const [sessionsInfo, setSessionsInfo] = useState(null); const [playerCharacters, setPlayerCharacters] = useState( [], ); const [selectedCharacter, setSelectedCharacter] = useState( null, ); const [showCharacterSelect, setShowCharacterSelect] = useState(false); const [isLoading, setIsLoading] = useState(true); useEffect(() => { const loadStory = async () => { if (!id || !isAuthenticated) { setIsLoading(false); return; } setIsLoading(true); const foundStory = await getStory(id); if (foundStory) { setStory({ ...foundStory, id: (foundStory as any)._id || foundStory.id, }); const sessions = await getSessionsList(id); if (sessions.length > 0) { const totalMessages = sessions.reduce((sum, s) => sum + s.messagesCount, 0); setSessionsInfo({ count: sessions.length, totalMessages, }); } } // Загружаем персонажей пользователя const characters = await getPlayerCharacters(); setPlayerCharacters(characters); setIsLoading(false); }; loadStory(); }, [id, isAuthenticated]); // Получаем имя персонажа-фаворита для замены {user} const favoriteCharacter = playerCharacters.find((c) => c.isFavorite); const replaceUserPlaceholder = (text: string) => { if (!favoriteCharacter) return text; return text.replace(/\{user\}/gi, favoriteCharacter.name); }; const handleDelete = async () => { if (story && confirm("Удалить эту историю и все связанные данные?")) { const success = await apiDeleteStory(story.id); if (success) { navigate("/"); } } }; const handleStartGame = () => { if (playerCharacters.length === 0) { // Нет персонажей — предлагаем создать if (confirm("У вас нет персонажей. Хотите создать персонажа для игры?")) { navigate("/characters"); } return; } if (!sessionsInfo) { // Новая игра — показываем выбор персонажа setShowCharacterSelect(true); } else { // Продолжаем с тем же персонажем navigate(`/play/${story!.id}`); } }; const handleConfirmCharacter = () => { if (!selectedCharacter) { alert("Выберите персонажа для игры"); return; } navigate(`/play/${story!.id}?character=${selectedCharacter}`); }; const formatTokens = (tokens: number) => { if (tokens >= 1000000) return `${(tokens / 1000000).toFixed(1)}M`; if (tokens >= 1000) return `${(tokens / 1000).toFixed(1)}K`; return tokens.toString(); }; if (isLoading) { return (

Загрузка...

); } if (!story) { return (

История не найдена

← Вернуться к списку
); } return (
← Назад к историям {/* Модальное окно выбора персонажа */} {showCharacterSelect && (

👤 Выберите персонажа

Выберите, за кого вы хотите играть в этой истории

{[...playerCharacters] .sort((a, b) => { if (a.isFavorite && !b.isFavorite) return -1; if (!a.isFavorite && b.isFavorite) return 1; return 0; }) .map((char) => (
setSelectedCharacter(char.id)} > {char.isFavorite && ( )}
{char.avatarUrl ? ( {char.name} ) : ( 👤 )}

{char.name}

{char.description && (

{char.description .replace(/\{user\}/gi, char.name) .substring(0, 80)} ...

)}
{selectedCharacter === char.id && (
)}
))}
+ Создать нового
)}
{story.isNsfw && 🔞 NSFW} {story.genre.map((g) => ( {g} ))}

{story.title}

{Array.isArray(story.setting) ? ( story.setting.map((s) => ( 🏰 {s} )) ) : ( 🏰 {story.setting} )}
{story.summary && (

{replaceUserPlaceholder(story.summary)}

)} {story.plot && (

📜 Сюжет

{replaceUserPlaceholder(story.plot) .split("\n") .map((line, i) => (

{line}

))}
)} {story.characters && story.characters.length > 0 && (

👥 Персонажи мира

{story.characters.map((char, i) => (

{char.name}

{char.role}

{char.description}

))}
)}

🌐 Мир: {story.world.name}

{story.world.description}

{story.world.rules.length > 0 && (
Правила мира:
    {story.world.rules.map((rule, i) => (
  • {rule}
  • ))}
)}
{sessionsInfo && (

🎮 Текущий прогресс

Сессий {sessionsInfo.count}
Сообщений {sessionsInfo.totalMessages}
≈ Токенов {formatTokens(sessionsInfo.totalMessages * 50)}
)}
✏️ Редактировать
Создано: {new Date(story.createdAt).toLocaleDateString("ru-RU")} Обновлено: {new Date(story.updatedAt).toLocaleDateString("ru-RU")}
); }