"use client"; import { useEffect, useState } from "react"; import { signIn, useSession } from "next-auth/react"; import { Play, ChevronLeft, Server, Music, Loader2, SearchX, MonitorPlay, Disc, ListMusic, LogIn } from "lucide-react"; import { ViewMode } from "@/app/page"; // 🌟 [좔가됨] μ „μ—­ ν† μŠ€νŠΈ ν›… 뢈러였기 import { useToast } from "@/components/ToastProvider"; import type { DiscordServer, SearchTrack, SearchResults } from "@/types/music"; /** ms -> m:ss (검색 κ²°κ³Ό μž¬μƒμ‹œκ°„ ν‘œκΈ°μš©) */ function formatDuration(ms?: number) { if (!ms || Number.isNaN(ms)) return ""; const total = Math.floor(ms / 1000); return `${Math.floor(total / 60)}:${(total % 60).toString().padStart(2, "0")}`; } interface MainContentProps { viewMode: ViewMode; setViewMode: (mode: ViewMode) => void; selectedServer: DiscordServer | null; setSelectedServer: (server: DiscordServer | null) => void; searchQuery: string; setSearchQuery: (query: string) => void; onSelectServer: (server: DiscordServer) => void; } export default function MainContent({ viewMode, setViewMode, selectedServer, setSelectedServer, searchQuery, setSearchQuery, onSelectServer, }: MainContentProps) { const { data: session, status } = useSession(); // 🌟 [좔가됨] 훅을 μ‹€ν–‰ν•΄μ„œ showToast ν•¨μˆ˜ κΊΌλ‚΄κΈ° const { showToast } = useToast(); const [servers, setServers] = useState([]); const [isFetching, setIsFetching] = useState(false); const [searchResults, setSearchResults] = useState({ spotify: [], youtubeMusic: [], youtubeVideo: [] }); const [isSearching, setIsSearching] = useState(false); const handleMusicAction = async (actionType: 'player_play' | 'player_playlist', track?: SearchTrack, playlistUrl?: string) => { if (!selectedServer) { // 🌟 alert -> showToast ꡐ체 showToast("λͺ…령을 내릴 λ””μŠ€μ½”λ“œ μ„œλ²„κ°€ μ„ νƒλ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€.", "error"); return; } const userId = session?.user?.id; if (!userId) { showToast("둜그인이 ν•„μš”ν•©λ‹ˆλ‹€.", "error"); return; } let endpoint = ""; const bodyData: Record = { serverId: selectedServer.id }; if (actionType === 'player_play') { endpoint = "/api/player/play"; bodyData.track = track; } else if (actionType === 'player_playlist') { endpoint = "/api/player/playlist"; bodyData.playlistUrl = playlistUrl; } try { const res = await fetch(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(bodyData), }); const data = await res.json(); if (res.ok && data.success) { // 🌟 성곡 μ‹œ μ΄ˆλ‘μƒ‰ μ•Œλ¦Ό showToast("μž¬μƒλͺ©λ‘μ— 곑을 μΆ”κ°€ν–ˆμŠ΅λ‹ˆλ‹€!", "success"); } else { // 🌟 μ—λŸ¬ μ‹œ 빨간색 μ•Œλ¦Ό showToast(data.message || data.error || "μš”μ²­ 처리 쀑 λ¬Έμ œκ°€ λ°œμƒν–ˆμŠ΅λ‹ˆλ‹€.", "error"); } } catch (error) { console.error("Music Action Error:", error); // 🌟 λ„€νŠΈμ›Œν¬ μ—λŸ¬ μ‹œ μ•Œλ¦Ό showToast('μ„œλ²„μ™€ 톡신할 수 μ—†μŠ΅λ‹ˆλ‹€.', "error"); } }; const getPermissionLabel = (server: DiscordServer | null) => { if (!server) return "μ•Œ 수 μ—†μŒ"; if (server.owner) return "πŸ‘‘ μ„œλ²„ 주인"; // Discord permissions λŠ” 큰 μ •μˆ˜ λ¬Έμžμ—΄λ‘œ 도착함. 숫자/λ¬Έμžμ—΄λ§Œ λ°›μ•„ μ•ˆμ „ν•˜κ²Œ BigInt ν™”. const raw: unknown = server.permissions; if (typeof raw !== "string" && typeof raw !== "number") return "πŸ‘€ 일반 멀버"; try { const perms = BigInt(raw); if ((perms & 0x8n) === 0x8n) return "πŸ› οΈ κ΄€λ¦¬μž"; if ((perms & 0x20n) === 0x20n) return "βš™οΈ λ§€λ‹ˆμ €"; return "πŸ‘€ 일반 멀버"; } catch { return "πŸ‘€ 일반 멀버"; } }; useEffect(() => { if (status === "authenticated") { setIsFetching(true); const cached = sessionStorage.getItem("filtered_servers"); if (cached) { try { const parsed = JSON.parse(cached); if (Array.isArray(parsed)) { setServers(parsed); setIsFetching(false); } } catch { // μΊμ‹œ 손상 μ‹œ λ¬΄μ‹œν•˜κ³  μ•„λž˜ fetch둜 μƒˆλ‘œ λ°›λŠ”λ‹€. sessionStorage.removeItem("filtered_servers"); } } fetch("/api/servers") .then((res) => res.json()) .then((data) => { if (Array.isArray(data)) { setServers(data); sessionStorage.setItem("filtered_servers", JSON.stringify(data)); } }) .finally(() => setIsFetching(false)); } }, [status]); useEffect(() => { if (viewMode === "SEARCH_RESULT" && searchQuery) { setIsSearching(true); setSearchResults({ spotify: [], youtubeMusic: [], youtubeVideo: [] }); fetch(`/api/search?q=${encodeURIComponent(searchQuery)}`) .then(async (res) => { const data = await res.json(); if (res.ok && data) { setSearchResults({ spotify: data.spotify || [], youtubeMusic: data.youtubeMusic || [], youtubeVideo: data.youtubeVideo || [] }); } else { console.error("Search Error:", data); } }) .catch(err => console.error("Fetch Error:", err)) .finally(() => setIsSearching(false)); } }, [viewMode, searchQuery]); if (status === "unauthenticated") { return (

λ””μŠ€μ½”λ“œλ‘œ μ‹œμž‘ν•˜κΈ°

λ‘œκ·ΈμΈν•˜λ©΄ λ‚΄κ°€ μ†ν•œ μ„œλ²„ 쀑 μŒμ•… 봇이 μžˆλŠ” μ„œλ²„κ°€ 보이고, λΈŒλΌμš°μ €μ—μ„œ λ°”λ‘œ λ…Έλž˜λ₯Ό κ²€μƒ‰Β·μž¬μƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

{/* μ•ˆλ‚΄λ§Œ ν•˜κ³  λ²„νŠΌμ΄ μ—†μœΌλ©΄ μ‚¬μš©μžκ°€ μš°μƒλ‹¨μ„ μ°Ύμ•„ ν—€λ§€κ²Œ λœλ‹€ β€” μ—¬κΈ°μ„œ λ°”λ‘œ λ‘œκ·ΈμΈμ‹œν‚¨λ‹€. */}
); } if (status === "loading") { return (
); } const hasAnyResults = searchResults.spotify.length > 0 || searchResults.youtubeMusic.length > 0 || searchResults.youtubeVideo.length > 0; // μΉ΄λ“œ 전체가 λˆŒλ¦¬λŠ” λ²„νŠΌμ΄λ‹€. μ˜ˆμ „μ—λŠ” hover μ‹œμ—λ§Œ λ‚˜νƒ€λ‚˜λŠ” μž‘μ€ μ•„μ΄μ½˜λ§Œ λˆŒλŸ¬μ•Ό ν•΄μ„œ // ν„°μΉ˜ κΈ°κΈ°(hover μ—†μŒ)μ—μ„œλŠ” μž¬μƒ 방법을 μ°ΎκΈ° μ–΄λ €μ› λ‹€. const renderTrackCard = (track: SearchTrack) => ( ); return (
{/* ν™”λ©΄ 1: μ„œλ²„ λͺ©λ‘ */} {viewMode === "SERVER_LIST" && ( <>

μ–΄λŠ μ„œλ²„μ—μ„œ λ“€μ„κΉŒμš”?

μŒμ•… 봇이 λ“€μ–΄κ°€ μžˆλŠ” μ„œλ²„λ§Œ λ³΄μž…λ‹ˆλ‹€.

{isFetching && servers.length === 0 ? (

μ„œλ²„ λͺ©λ‘μ„ λΆˆλŸ¬μ˜€λŠ” 쀑...

) : servers.length === 0 ? (

ν‘œμ‹œν•  μ„œλ²„κ°€ μ—†μŠ΅λ‹ˆλ‹€.

λ‚΄κ°€ μ†ν•œ μ„œλ²„ 쀑 μŒμ•… 봇이 λ“€μ–΄κ°€ μžˆλŠ” 곳이 μ—†κ±°λ‚˜, 봇이 아직 μ€€λΉ„ 쀑일 수 μžˆμŠ΅λ‹ˆλ‹€.

) : (
{servers.map((server) => ( ))}
)} )} {/* ν™”λ©΄ 2: μ„œλ²„ 상세 λŒ€μ‹œλ³΄λ“œ */} {viewMode === "SERVER_DETAIL" && selectedServer && (
{selectedServer.icon ? ( // eslint-disable-next-line @next/next/no-img-element ) : }
μ„œλ²„ λŒ€μ‹œλ³΄λ“œ

{selectedServer.name}

μœ„ κ²€μƒ‰μ°½μ—μ„œ λ…Έλž˜λ₯Ό μ°Ύμ•„ λ°”λ‘œ μž¬μƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

{/* 무엇을 ν•΄μ•Ό ν•˜λŠ”μ§€ μ•Œλ €μ£ΌλŠ” μ•ˆλ‚΄ β€” μ„œλ²„λ§Œ κ³ λ₯΄λ©΄ λ‹€μŒ 행동이 λ§‰λ§‰ν–ˆλ˜ 문제 보완 */}

λ¨Όμ € λ””μŠ€μ½”λ“œμ—μ„œ μŒμ„± 채널에 μž…μž₯ν•œ λ’€, λ…Έλž˜λ₯Ό 검색해 μž¬μƒ λ²„νŠΌμ„ λˆ„λ₯΄μ„Έμš”.

상세 정보

μ„œλ²„ 고유 ID

{selectedServer.id}

λ‚˜μ˜ μ„œλ²„ κΆŒν•œ

{getPermissionLabel(selectedServer)}

)} {/* ν™”λ©΄ 3: 검색 κ²°κ³Ό λͺ©λ‘ (3κ°€μ§€ μΉ΄ν…Œκ³ λ¦¬λ‘œ λΆ„ν• ) */} {viewMode === "SEARCH_RESULT" && (

“{searchQuery}” 검색 κ²°κ³Ό

곑을 λˆ„λ₯΄λ©΄ λŒ€κΈ°μ—΄μ— μΆ”κ°€λ©λ‹ˆλ‹€.

{isSearching ? (

λ…Έλž˜λ₯Ό μ°ΎλŠ” μ€‘μž…λ‹ˆλ‹€...

) : !hasAnyResults ? (

검색 κ²°κ³Όκ°€ μ—†μŠ΅λ‹ˆλ‹€.

) : (
{/* μΉ΄ν…Œκ³ λ¦¬ 1: Spotify */} {searchResults.spotify.length > 0 && (

Spotify κ²°κ³Ό

{searchResults.spotify.map(renderTrackCard)}
)} {/* μΉ΄ν…Œκ³ λ¦¬ 2: YouTube Music */} {searchResults.youtubeMusic.length > 0 && (

YouTube Music

{searchResults.youtubeMusic.map(renderTrackCard)}
)} {/* μΉ΄ν…Œκ³ λ¦¬ 3: YouTube Video */} {searchResults.youtubeVideo.length > 0 && (

YouTube μ˜μƒ

{searchResults.youtubeVideo.map(renderTrackCard)}
)}
)}
)}
); }