/* global React, ReactDOM */ const { useEffect, useRef, useState, useMemo } = React; const I = { arrowLeft: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'m15 18-6-6 6-6'})), arrowUp: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'m18 15-6-6-6 6'})), arrowDown: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'m6 9 6 6 6-6'})), ban: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('circle',{cx:12,cy:12,r:10}),React.createElement('path',{d:'m4.9 4.9 14.2 14.2'})), bell: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9'}),React.createElement('path',{d:'M10.3 21a1.94 1.94 0 0 0 3.4 0'})), bellOff: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M8.7 3A6 6 0 0 1 18 8a21.3 21.3 0 0 0 .6 5'}),React.createElement('path',{d:'M12 12H3'}),React.createElement('path',{d:'m1 1 2 2'}),React.createElement('path',{d:'M20.56 15H3'}),React.createElement('path',{d:'m16 19 2 2'}),React.createElement('path',{d:'m17 21 .5-1.5M17.5 4 .5 2.5M1 15h9'})), bot: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M12 8V4H8'}),React.createElement('rect',{width:16,height:12,x:4,y:8,rx:2}),React.createElement('path',{d:'M2 14h2'}),React.createElement('path',{d:'M20 14h2'}),React.createElement('path',{d:'M15 13v2'}),React.createElement('path',{d:'M9 13v2'})), check: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M20 6 9 17l-5-5'})), chevronDown: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'m6 9 6 6 6-6'})), clipboardList: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('rect',{width:8,height:4,x:8,y:2,rx:1,ry:1}),React.createElement('path',{d:'M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2'}),React.createElement('path',{d:'M12 11h4'}),React.createElement('path',{d:'M12 16h4'}),React.createElement('path',{d:'M8 11h.01'}),React.createElement('path',{d:'M8 16h.01'})), flame: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M8.5 14.5A2.5 2.5 0 0 0 11 12c0-1.38-.5-2-1-3-1.072-2.143-.224-4.054 2-6 .5 2.5 2 4.9 4 6.5 2 1.6 3 3.5 3 5.5a7 7 0 1 1-14 0c0-1.153.433-2.294 1-3a2.5 2.5 0 0 0 2.5 2.5z'})), globe: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('circle',{cx:12,cy:12,r:10}),React.createElement('path',{d:'M12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20'}),React.createElement('path',{d:'M2 12h20'})), headphones: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M3 14h3a2 2 0 0 1 2 2v3a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-7a9 9 0 0 1 18 0v7a2 2 0 0 1-2 2h-1a2 2 0 0 1-2-2v-3a2 2 0 0 1 2-2h3'})), image: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('rect',{width:18,height:18,x:3,y:3,rx:2,ry:2}),React.createElement('circle',{cx:9,cy:9,r:2}),React.createElement('path',{d:'m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21'})), layout: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('rect',{width:18,height:18,x:3,y:3,rx:2}),React.createElement('path',{d:'M3 9h18'}),React.createElement('path',{d:'M9 21V9'})), listMusic: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M21 15V6'}),React.createElement('path',{d:'M18.5 18a2.5 2.5 0 1 0 0-5 2.5 2.5 0 0 0 0 5Z'}),React.createElement('path',{d:'M12 12H3'}),React.createElement('path',{d:'M16 6H3'}),React.createElement('path',{d:'M12 18H3'})), lock: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('rect',{width:18,height:11,x:3,y:11,rx:2,ry:2}),React.createElement('path',{d:'M7 11V7a5 5 0 0 1 10 0v4'})), logOut: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4'}),React.createElement('polyline',{points:'16,17 21,12 16,7'}),React.createElement('line',{x1:21,x2:9,y1:12,y2:12})), menu: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('line',{x1:4,x2:20,y1:12,y2:12}),React.createElement('line',{x1:4,x2:20,y1:6,y2:6}),React.createElement('line',{x1:4,x2:20,y1:18,y2:18})), messageCircle: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M7.9 20A9 9 0 1 0 4 16.1L2 22Z'})), mic: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z'}),React.createElement('path',{d:'M19 10v2a7 7 0 0 1-14 0v-2'}),React.createElement('line',{x1:12,x2:12,y1:19,y2:22})), moreH: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('circle',{cx:12,cy:12,r:1}),React.createElement('circle',{cx:19,cy:12,r:1}),React.createElement('circle',{cx:5,cy:12,r:1})), paperclip: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'m21.44 11.05-9.19 9.19a6 6 0 0 1-8.49-8.49l8.57-8.57A4 4 0 1 1 18 8.84l-8.59 8.57a2 2 0 0 1-2.83-2.83l8.49-8.48'})), phone: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M22 16.92v3a2 2 0 0 1-2.18 2 19.79 19.79 0 0 1-8.63-3.07 19.5 19.5 0 0 1-6-6 19.79 19.79 0 0 1-3.07-8.67A2 2 0 0 1 4.11 2h3a2 2 0 0 1 2 1.72c.127.96.361 1.903.7 2.81a2 2 0 0 1-.45 2.11L8.09 9.91a16 16 0 0 0 6 6l1.27-1.27a2 2 0 0 1 2.11-.45c.907.339 1.85.573 2.81.7A2 2 0 0 1 22 16.92z'})), pin: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('line',{x1:12,x2:12,y1:17,y2:22}),React.createElement('path',{d:'M5 17h14v-1.76a2 2 0 0 0-1.11-1.79l-1.78-.9A2 2 0 0 1 15 10.76V6h1a2 2 0 0 0 0-4H8a2 2 0 0 0 0 4h1v4.76a2 2 0 0 1-1.11 1.79l-1.78.9A2 2 0 0 0 5 15.24Z'})), plus: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M5 12h14'}),React.createElement('path',{d:'M12 5v14'})), radio: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M4.9 19.1C1 15.2 1 8.8 4.9 4.9'}),React.createElement('path',{d:'M7.8 16.2c-2.3-2.3-2.3-6.1 0-8.5'}),React.createElement('circle',{cx:12,cy:12,r:2}),React.createElement('path',{d:'M16.2 7.8c2.3 2.3 2.3 6.1 0 8.5'}),React.createElement('path',{d:'M19.1 4.9C23 8.8 23 15.1 19.1 19'})), search: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('circle',{cx:11,cy:11,r:8}),React.createElement('path',{d:'m21 21-4.3-4.3'})), send: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'m22 2-7 20-4-9-9-4Z'}),React.createElement('path',{d:'M22 2 11 13'})), settings: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z'}),React.createElement('circle',{cx:12,cy:12,r:3})), smile: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('circle',{cx:12,cy:12,r:10}),React.createElement('path',{d:'M8 14s1.5 2 4 2 4-2 4-2'}),React.createElement('line',{x1:9,x2:9.01,y1:9,y2:9}),React.createElement('line',{x1:15,x2:15.01,y1:9,y2:9})), userRound: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M18 21a8 8 0 0 0-16 0'}),React.createElement('circle',{cx:10,cy:8,r:5})), userRoundPlus: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2'}),React.createElement('circle',{cx:9,cy:7,r:4}),React.createElement('line',{x1:19,x2:19,y1:8,y2:14}),React.createElement('line',{x1:22,x2:16,y1:11,y2:11})), users: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2'}),React.createElement('circle',{cx:9,cy:7,r:4}),React.createElement('path',{d:'M22 21v-2a4 4 0 0 0-3-3.87'}),React.createElement('path',{d:'M16 3.13a4 4 0 0 1 0 7.75'})), volume2: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('polygon',{points:'11,5 6,9 2,9 2,15 6,15 11,19'}),React.createElement('path',{d:'M15.54 8.46a5 5 0 0 1 0 7.07'}),React.createElement('path',{d:'M19.07 4.93a10 10 0 0 1 0 14.14'})), volumeX: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('polygon',{points:'11,5 6,9 2,9 2,15 6,15 11,19'}),React.createElement('line',{x1:22,x2:16,y1:9,y2:15}),React.createElement('line',{x1:16,x2:22,y1:9,y2:15})), video: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'m16 13 5.223 3.482a.5.5 0 0 0 .777-.416V7.87a.5.5 0 0 0-.752-.432L16 10.5'}),React.createElement('rect',{x:2,y:6,width:14,height:12,rx:2})), warning: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z'}),React.createElement('path',{d:'M12 9v4'}),React.createElement('path',{d:'M12 17h.01'})), x: (s=18) => React.createElement('svg',{width:s,height:s,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:2,strokeLinecap:'round',strokeLinejoin:'round'},React.createElement('path',{d:'M18 6 6 18'}),React.createElement('path',{d:'m6 6 12 12'})), }; const API_URL = "/api.php"; async function apiRequest(action, options = {}) { const { method = "GET", body, query = {} } = options; const params = new URLSearchParams({ action, ...query }); const response = await fetch(`${API_URL}?${params.toString()}`, { method, headers: body instanceof FormData ? { Accept: "application/json" } : body ? { "Content-Type": "application/json", Accept: "application/json" } : { Accept: "application/json" }, credentials: "include", body: body instanceof FormData ? body : body ? JSON.stringify(body) : undefined, }); const payload = await response.json().catch(() => ({})); if (response.status === 503 && action === "me") return { ok: true, user: null, backendUnavailable: true }; if (!response.ok || payload.ok === false) { const error = new Error(payload.message || payload.error || "İşlem tamamlanamadı."); error.code = payload.error; throw error; } return payload; } function Avatar({ person, size = "md", showStatus = false }) { const src = person.avatar_url || person.avatar; const isGif = src && src.toLowerCase().endsWith('.gif'); return ( {src ? {person.display_name : {(person.display_name || person.name || '??').slice(0, 2).toUpperCase()}} {showStatus && } ); } function RoleBadge({ role }) { const config = { root: { label: 'ROOT', color: '#ff4444', bg: 'rgba(255,68,68,.15)' }, super_admin: { label: 'SÜPER ADMİN', color: '#ff6b00', bg: 'rgba(255,107,0,.15)' }, admin: { label: 'ADMİN', color: '#e74c3c', bg: 'rgba(231,76,60,.15)' }, moderator: { label: 'MOD', color: '#3498db', bg: 'rgba(52,152,219,.15)' }, }; const c = config[role]; if (!c) return null; return {c.label}; } function getRoleColor(role) { return { root: '#ff4444', super_admin: '#ff6b00', admin: '#e74c3c', moderator: '#3498db', user: '#2ecc71' }[role] || '#95a5a6'; } function Modal({ children, title, onClose, wide = false }) { return (
event.stopPropagation()}>

{title}

{children}
); } function App() { const [activeTab, setActiveTab] = useState("chats"); const [selectedId, setSelectedId] = useState(null); const [search, setSearch] = useState(""); const [messages, setMessages] = useState({}); const [draft, setDraft] = useState(""); const [showSearch, setShowSearch] = useState(false); const [showNotifications, setShowNotifications] = useState(false); const [showAdmin, setShowAdmin] = useState(false); const [showAccount, setShowAccount] = useState(false); const [showSettings, setShowSettings] = useState(false); const [liveUser, setLiveUser] = useState(null); const [authMode, setAuthMode] = useState("login"); const [authForm, setAuthForm] = useState({ identity: "", password: "", display_name: "", username: "", email: "", gender: "other" }); const [authError, setAuthError] = useState(""); const [callType, setCallType] = useState(null); const [mobileSidebar, setMobileSidebar] = useState(false); const [toast, setToast] = useState(""); const [roomFilter, setRoomFilter] = useState("Tümü"); const [conversations, setConversations] = useState([]); const [rooms, setRooms] = useState([]); const [onlineUsers, setOnlineUsers] = useState([]); const [adminTab, setAdminTab] = useState("dashboard"); const [adminStats, setAdminStats] = useState(null); const [adminUsers, setAdminUsers] = useState([]); const [adminRooms, setAdminRooms] = useState([]); const [adminLogs, setAdminLogs] = useState([]); const [adminReports, setAdminReports] = useState([]); const [adminSettings, setAdminSettings] = useState({}); const [adminBots, setAdminBots] = useState([]); const [adminRadio, setAdminRadio] = useState([]); const [adminAnnouncements, setAdminAnnouncements] = useState([]); const [adminIpBans, setAdminIpBans] = useState([]); const [adminSearch, setAdminSearch] = useState(""); const [adminRoleFilter, setAdminRoleFilter] = useState(""); const [showBotModal, setShowBotModal] = useState(false); const [showRadioModal, setShowRadioModal] = useState(false); const [showAnnouncementModal, setShowAnnouncementModal] = useState(false); const [showVoiceModal, setShowVoiceModal] = useState(false); const [selectedRoom, setSelectedRoom] = useState(null); const [voiceChannels, setVoiceChannels] = useState([]); const [currentVoiceChannel, setCurrentVoiceChannel] = useState(null); const [myBots, setMyBots] = useState([]); const [radioStations, setRadioStations] = useState([]); const [announcements, setAnnouncements] = useState([]); const [selectedUser, setSelectedUser] = useState(null); const [showProfileModal, setShowProfileModal] = useState(false); const [showAvatarUpload, setShowAvatarUpload] = useState(false); const [avatarPreview, setAvatarPreview] = useState(null); const [chatClosed, setChatClosed] = useState(false); const [botForm, setBotForm] = useState({ bot_name: "", username: "", description: "" }); const [autoReplyForm, setAutoReplyForm] = useState({ trigger_keyword: "", reply_text: "" }); const [selectedBot, setSelectedBot] = useState(null); const [radioForm, setRadioForm] = useState({ name: "", stream_url: "", genre: "" }); const [announcementForm, setAnnouncementForm] = useState({ title: "", body: "", priority: "normal" }); const [voiceForm, setVoiceForm] = useState({ name: "", channel_type: "voice" }); const [userSearch, setUserSearch] = useState(""); const [muted, setMuted] = useState(false); const [blocked, setBlocked] = useState(false); const [followed, setFollowed] = useState(false); const selected = conversations.find((c) => c.user_id === selectedId || c.id === selectedId); const selectedMessages = messages[selected?.id || selectedId] || []; const messagesEndRef = useRef(null); useEffect(() => { apiRequest("me").then((r) => { if (r.user) { setLiveUser(r.user); loadConversations(); loadRooms(); loadOnlineUsers(); } }).catch(() => {}); }, []); useEffect(() => { if (!liveUser) return; loadConversations(); loadRooms(); loadOnlineUsers(); const interval = setInterval(() => { apiRequest("heartbeat").catch(() => {}); loadOnlineUsers(); loadConversations(); }, 30000); return () => clearInterval(interval); }, [liveUser?.id]); useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }, [selectedMessages.length]); function notify(message) { setToast(message); window.setTimeout(() => setToast(""), 2200); } async function loadConversations() { try { const r = await apiRequest("conversations"); setConversations(r.items || []); } catch {} } async function loadRooms() { try { const r = await apiRequest("rooms"); setRooms(r.items || []); } catch {} } async function loadOnlineUsers() { try { const r = await apiRequest("online_users"); setOnlineUsers(r.items || []); } catch {} } async function loadMessages(convId) { try { const r = await apiRequest("messages", { query: { conversation_id: convId } }); setMessages((prev) => ({ ...prev, [convId]: r.items || [] })); } catch {} } async function loadAdminData(tab) { try { if (tab === "dashboard") { const r = await apiRequest("admin_stats"); setAdminStats(r.stats); } else if (tab === "users") { const r = await apiRequest("admin_users", { query: { search: adminSearch, role: adminRoleFilter } }); setAdminUsers(r.items || []); } else if (tab === "rooms") { const r = await apiRequest("admin_rooms"); setAdminRooms(r.items || []); } else if (tab === "logs") { const r = await apiRequest("admin_logs"); setAdminLogs(r.items || []); } else if (tab === "reports") { const r = await apiRequest("admin_reports"); setAdminReports(r.items || []); } else if (tab === "settings") { const r = await apiRequest("admin_settings"); setAdminSettings(r.settings || {}); } else if (tab === "bots") { const r = await apiRequest("admin_bots"); setAdminBots(r.items || []); } else if (tab === "radio") { const r = await apiRequest("admin_radio_stations"); setAdminRadio(r.items || []); } else if (tab === "announcements") { const r = await apiRequest("admin_announcements"); setAdminAnnouncements(r.items || []); } else if (tab === "ipbans") { const r = await apiRequest("admin_ip_bans"); setAdminIpBans(r.items || []); } } catch (e) { notify("Hata: " + e.message); } } async function loadAnnouncements() { try { const r = await apiRequest("announcements"); setAnnouncements(r.items || []); } catch {} } async function loadMyBots() { try { const r = await apiRequest("my_bots"); setMyBots(r.items || []); } catch {} } async function loadRadioStations() { try { const r = await apiRequest("radio_stations"); setRadioStations(r.items || []); } catch {} } function selectConversation(id) { setSelectedId(id); setActiveTab("chats"); setMobileSidebar(false); const conv = conversations.find((c) => c.id === id || c.user_id === id); if (conv) { loadMessages(conv.id); setChatClosed(!!conv.is_closed); } } async function sendMessage(event) { event?.preventDefault(); const text = draft.trim(); if (!text || !selected) return; try { const r = await apiRequest("send_message", { method: "POST", body: { conversation_id: selected.id, body: text } }); setMessages((prev) => ({ ...prev, [selected.id]: [...(prev[selected.id] || []), { id: r.id, sender_id: liveUser.id, body: text, message_type: "text", color: getRoleColor(liveUser.role), created_at: new Date().toISOString(), display_name: liveUser.display_name, username: liveUser.username }], })); setDraft(""); } catch (e) { notify("Mesaj gönderilemedi: " + e.message); } } function startCall(type) { setCallType(type); notify(`${type === "voice" ? "Sesli" : "Görüntülü"} arama başlatılıyor`); } async function submitAuth(event) { event.preventDefault(); setAuthError(""); try { const result = await apiRequest(authMode, { method: "POST", body: authMode === "login" ? { identity: authForm.identity, password: authForm.password } : authForm, }); setLiveUser(result.user); setShowAccount(false); notify(authMode === "login" ? "Giriş yapıldı" : "Hesabın oluşturuldu"); loadConversations(); loadRooms(); } catch (error) { setAuthError(error.message); } } async function submitGoogleLogin() { try { const result = await apiRequest("google_login", { method: "POST", body: { google_id: "demo_" + Date.now(), email: "demo@novatalk.app", display_name: "Google Kullanıcı", avatar_url: "" } }); setLiveUser(result.user); setShowAccount(false); notify("Google ile giriş yapıldı"); loadConversations(); loadRooms(); } catch (error) { setAuthError(error.message); } } async function handleAvatarUpload(event) { const file = event.target.files[0]; if (!file) return; const formData = new FormData(); formData.append("avatar", file); try { const response = await fetch("/api.php?action=upload_avatar", { method: "POST", body: formData, credentials: "include" }); const result = await response.json(); if (result.ok) { setLiveUser((prev) => ({ ...prev, avatar_url: result.avatar_url })); notify("Avatar güncellendi"); setShowAvatarUpload(false); } else { notify("Yükleme başarısız: " + (result.error || "")); } } catch { notify("Yükleme hatası"); } } async function openAdminPanel() { setShowAdmin(true); setAdminTab("dashboard"); loadAdminData("dashboard"); } async function switchAdminTab(tab) { setAdminTab(tab); loadAdminData(tab); } async function updateAdminSetting(key, value) { try { await apiRequest("admin_settings", { method: "POST", body: { [key]: value } }); setAdminSettings((prev) => ({ ...prev, [key]: value })); notify("Ayar güncellendi"); } catch (e) { notify("Hata: " + e.message); } } async function createBot() { try { await apiRequest("create_bot", { method: "POST", body: botForm }); notify("Bot oluşturuldu"); setShowBotModal(false); setBotForm({ bot_name: "", username: "", description: "" }); loadAdminData("bots"); } catch (e) { notify("Hata: " + e.message); } } async function addAutoReply(botId) { try { await apiRequest("add_auto_reply", { method: "POST", body: { bot_id: botId, ...autoReplyForm } }); notify("Otomatik cevap eklendi"); setAutoReplyForm({ trigger_keyword: "", reply_text: "" }); } catch (e) { notify("Hata: " + e.message); } } async function addRadioStation() { try { await apiRequest("add_radio_station", { method: "POST", body: radioForm }); notify("Radyo istasyonu eklendi"); setShowRadioModal(false); setRadioForm({ name: "", stream_url: "", genre: "" }); } catch (e) { notify("Hata: " + e.message); } } async function createAnnouncement() { try { await apiRequest("admin_announcements", { method: "POST", body: announcementForm }); notify("Duyuru oluşturuldu"); setShowAnnouncementModal(false); setAnnouncementForm({ title: "", body: "", priority: "normal" }); loadAdminData("announcements"); } catch (e) { notify("Hata: " + e.message); } } async function createVoiceChannel() { if (!selectedRoom) return; try { await apiRequest("create_voice_channel", { method: "POST", body: { room_id: selectedRoom.id, ...voiceForm } }); notify("Sesli kanal oluşturuldu"); setShowVoiceModal(false); setVoiceForm({ name: "", channel_type: "voice" }); loadVoiceChannels(selectedRoom.id); } catch (e) { notify("Hata: " + e.message); } } async function loadVoiceChannels(roomId) { try { const r = await apiRequest("voice_channels", { query: { room_id: roomId } }); setVoiceChannels(r.items || []); } catch {} } async function joinVoiceChannel(channelId) { try { await apiRequest("join_voice_channel", { method: "POST", body: { channel_id: channelId } }); setCurrentVoiceChannel(channelId); notify("Sesli kanala katıldın"); } catch (e) { notify("Hata: " + e.message); } } async function leaveVoiceChannel() { try { await apiRequest("leave_voice_channel", { method: "POST" }); setCurrentVoiceChannel(null); notify("Sesli kanaldan ayrıldın"); } catch {} } async function updateUserRole(userId, role) { try { await apiRequest("admin_update_user", { method: "POST", body: { user_id: userId, role } }); notify("Kullanıcı rolü güncellendi"); loadAdminData("users"); } catch (e) { notify("Hata: " + e.message); } } async function toggleUserBan(userId, ban) { try { await apiRequest("admin_update_user", { method: "POST", body: { user_id: userId, is_banned: ban } }); notify(ban ? "Kullanıcı engellendi" : "Engel kaldırıldı"); loadAdminData("users"); } catch (e) { notify("Hata: " + e.message); } } async function toggleUserActive(userId, active) { try { await apiRequest("admin_update_user", { method: "POST", body: { user_id: userId, is_active: active } }); notify(active ? "Kullanıcı aktif edildi" : "Kullanıcı deaktif edildi"); loadAdminData("users"); } catch (e) { notify("Hata: " + e.message); } } async function warnUser(userId, reason, severity) { try { await apiRequest("admin_warn_user", { method: "POST", body: { user_id: userId, reason, severity } }); notify("Kullanıcı uyarıldı"); } catch (e) { notify("Hata: " + e.message); } } function renderAdminPanel() { if (!liveUser || !["admin", "super_admin", "root", "moderator"].includes(liveUser.role)) return null; const isRoot = liveUser.role === "root"; const isSuperAdmin = ["super_admin", "root"].includes(liveUser.role); const tabs = [ { id: "dashboard", label: "Gösterge Paneli", icon: I.layout(15) }, { id: "users", label: "Kullanıcılar", icon: I.users(15) }, { id: "rooms", label: "Odalar", icon: I.radio(15) }, { id: "bots", label: "Botlar", icon: I.bot(15) }, { id: "radio", label: "Radyo", icon: I.headphones(15) }, { id: "reports", label: "Raporlar", icon: I.warning(15) }, { id: "logs", label: "Loglar", icon: I.clipboardList(15) }, ]; if (isSuperAdmin) tabs.push({ id: "announcements", label: "Duyurular", icon: I.bell(15) }); if (isRoot) tabs.push({ id: "settings", label: "Ayarlar", icon: I.settings(15) }, { id: "ipbans", label: "IP Ban", icon: I.ban(15) }); return ( setShowAdmin(false)}>
{tabs.map((t) => ( ))}
{adminTab === "dashboard" && adminStats && (
I.users(20)
{adminStats.users}Toplam Üye
I.globe(20)
{adminStats.online}Çevrimiçi
I.radio(20)
{adminStats.rooms}Canlı Oda
{I.messageCircle(20)}
{adminStats.messages_today}Bugünkü Mesaj
I.bot(20)
{adminStats.bots}Aktif Bot
I.headphones(20)
{adminStats.radio_stations}Radyo İstasyonu
I.warning(20)
{adminStats.reports_open}Açık Rapor
I.ban(20)
{adminStats.banned_users}Engellenen
)} {adminTab === "users" && (
setAdminSearch(e.target.value)} onKeyDown={(e) => e.key === "Enter" && loadAdminData("users")} />
{adminUsers.map((u) => (
{u.display_name} @{u.username} · {u.email}
{isSuperAdmin && ( )}
))}
)} {adminTab === "rooms" && (
{adminRooms.map((r) => (
{r.name} {r.category} · {r.member_count} üye · {r.host_name} {r.is_live ? "🟢 Canlı" : "⚫ Kapalı"} · {r.is_hidden ? "Gizli" : "Açık"}
))}
)} {adminTab === "bots" && (
{adminBots.map((b) => (
{I.bot(20)}
{b.bot_name} @{b.username} · Sahibi: {b.owner_name} {b.is_active ? "Aktif" : "Pasif"} · {b.reply_count || 0} otomatik cevap
))}
)} {adminTab === "radio" && (
{adminRadio.map((s) => (
{I.headphones(18)}
{s.name} {s.genre || 'Genel'} · {s.vote_count} oy · Ekleyen: {s.added_by_name}
))}
)} {adminTab === "reports" && (
{adminReports.length === 0 &&
Açık rapor yok
} {adminReports.map((r) => (
{r.reason} Raporlayan: {r.reporter_name} · Durum: {r.status} {r.created_at}
))}
)} {adminTab === "logs" && (
{adminLogs.map((l) => (
{l.action}
{l.entity_type} #{l.entity_id || '—'} {l.actor_name} · {l.ip_address || '—'} {l.created_at}
))}
)} {adminTab === "announcements" && (
{adminAnnouncements.map((a) => (
{a.title} {a.body} Öncelik: {a.priority} · {a.created_by_name} · {a.created_at}
))}
)} {adminTab === "settings" && isRoot && (
setAdminSettings({ ...adminSettings, site_name: e.target.value })} />
setAdminSettings({ ...adminSettings, max_upload_mb: e.target.value })} />
setAdminSettings({ ...adminSettings, welcome_message: e.target.value })} />
)} {adminTab === "ipbans" && isRoot && (
{adminIpBans.map((b) => (
{b.ip_address} {b.reason || '—'} · {b.banned_by_name}
))}
)}
); } return (
novatalk
Canlı {onlineUsers.length} kişi çevrimiçi
{selected ? ( <>

{selected.display_name}

{selected.is_online ? "şimdi çevrimiçi" : "çevrimdışı"} · @{selected.username}

I.lock(12) Mesajların uçtan uca korunuyor
{chatClosed &&
I.lock(16) Bu sohbet yönetici tarafından kapatıldı
}
{selectedMessages.map((message) => (
{message.sender_id !== liveUser?.id && }
{message.sender_id !== liveUser?.id && {message.display_name}}
{message.message_type === "gif" ? GIF : message.body}
{message.created_at ? new Date(message.created_at).toLocaleTimeString('tr-TR', { hour: '2-digit', minute: '2-digit' }) : ''} {message.is_pinned && I.pin(11)}
))}
setDraft(e.target.value)} placeholder={chatClosed ? "Sohbet kapatıldı" : `${selected.display_name} kişisine mesaj yaz...`} disabled={chatClosed} /> {draft.trim() ? : }
) : (
I.messageCircle(48)

Nova Talk'a Hoş Geldin

Sohbet etmeye başlamak için sol panelden bir konuşma seç veya yeni biriyle tanış.

{announcements.length > 0 && (
I.bell(16) {announcements[0].title} {announcements[0].body}
)}
)}
{showSearch && ( setShowSearch(false)}>
I.search(18) setSearch(e.target.value)} />
{search ? "Sonuçlar" : "Son aramalar"}
{conversations.filter((c) => !search || `${c.display_name} ${c.username}`.toLowerCase().includes(search.toLowerCase())).slice(0, 5).map((c) => ( ))}
)} {showNotifications && ( setShowNotifications(false)}>
{announcements.length === 0 &&
Bildirim yok
} {announcements.map((a) => (
I.bell(16) {a.title}{a.body}
))}
)} {showAccount && ( setShowAccount(false)}> {liveUser ? ( <>
{liveUser.display_name} @{liveUser.username} Çevrimiçi
) : (
I.lock(18)
{authMode === "login" ? "Hesabına giriş yap" : "Nova Talk'a katıl"}PHP sunucusunda güvenle saklanır.
{authMode === "register" && <>} {authMode === "login" ? : null} {authError &&
{authError}
}
veya
)}
)} {showSettings && ( setShowSettings(false)}>

Profil

Bildirimler

Sesli bildirimler
)} {showAdmin && renderAdminPanel()} {showBotModal && ( setShowBotModal(false)}>