Spaces:
Sleeping
Sleeping
| document.addEventListener('DOMContentLoaded', async () => { | |
| let currentUser = null; | |
| let supabase; | |
| let ws; | |
| let pingInterval; | |
| // --- 0. INIT --- | |
| try { | |
| const config = await (await fetch('/api/config')).json(); | |
| supabase = window.supabase.createClient(config.supabase_url, config.supabase_key); | |
| } catch(e) { console.error("Config failed", e); } | |
| // --- 1. LOGIN --- | |
| document.getElementById('login-form').addEventListener('submit', async (e) => { | |
| e.preventDefault(); | |
| const u = document.getElementById('login-user').value.trim(); | |
| const p = document.getElementById('login-pass').value.trim(); | |
| const { data: user } = await supabase.from('users').select('*').eq('username', u).single(); | |
| if (user && dcodeIO.bcrypt.compareSync(p, user.password_hash)) { | |
| currentUser = user; | |
| document.getElementById('login-modal').classList.remove('open'); | |
| document.getElementById('app-interface').classList.remove('hidden'); | |
| document.getElementById('current-username').textContent = user.username; | |
| // Инициализация чекбокса настроек | |
| document.getElementById('setting-discovery').checked = user.is_looking_for_friends || false; | |
| initApp(); | |
| } else { | |
| const err = document.getElementById('login-error'); | |
| err.textContent = "INVALID ACCESS"; err.classList.remove('hidden'); | |
| } | |
| }); | |
| // --- 2. TABS LOGIC --- | |
| const navBtns = document.querySelectorAll('.nav-btn[data-tab]'); | |
| const viewChats = document.getElementById('view-chats'); | |
| const viewDiscovery = document.getElementById('view-discovery'); | |
| const sidebarTitle = document.getElementById('sidebar-title'); | |
| navBtns.forEach(btn => { | |
| btn.addEventListener('click', () => { | |
| // Remove active class | |
| navBtns.forEach(b => b.classList.remove('active')); | |
| btn.classList.add('active'); | |
| const tab = btn.dataset.tab; | |
| if (tab === 'chats') { | |
| viewChats.classList.remove('hidden'); | |
| viewDiscovery.classList.add('hidden'); | |
| sidebarTitle.textContent = "CHANNELS"; | |
| } else if (tab === 'discovery') { | |
| viewChats.classList.add('hidden'); | |
| viewDiscovery.classList.remove('hidden'); | |
| sidebarTitle.textContent = "FIND FRIENDS"; | |
| loadDiscoveryUsers(); | |
| } | |
| }); | |
| }); | |
| // --- 3. DISCOVERY & SETTINGS --- | |
| const settingsModal = document.getElementById('settings-modal'); | |
| document.getElementById('open-settings').addEventListener('click', () => settingsModal.classList.add('open')); | |
| document.getElementById('save-settings').addEventListener('click', async () => { | |
| const isLooking = document.getElementById('setting-discovery').checked; | |
| // Update DB | |
| await supabase.from('users').update({ is_looking_for_friends: isLooking }).eq('id', currentUser.id); | |
| currentUser.is_looking_for_friends = isLooking; // local update | |
| settingsModal.classList.remove('open'); | |
| if(isLooking) alert("You are now visible in Discovery!"); | |
| }); | |
| async function loadDiscoveryUsers() { | |
| const list = document.getElementById('discovery-list'); | |
| list.innerHTML = '<div class="text-white/30 text-xs italic">Scanning sector...</div>'; | |
| const { data: users } = await supabase | |
| .from('users') | |
| .select('*') | |
| .eq('is_looking_for_friends', true) | |
| .neq('username', currentUser.username); // Exclude self | |
| list.innerHTML = ''; | |
| if (!users || users.length === 0) { | |
| list.innerHTML = '<div class="text-white/30 text-xs p-2">No signals found.</div>'; | |
| return; | |
| } | |
| users.forEach(u => { | |
| const div = document.createElement('div'); | |
| div.className = 'flex items-center justify-between p-2 bg-white/5 rounded hover:bg-white/10 transition cursor-pointer'; | |
| div.innerHTML = ` | |
| <div class="flex items-center gap-2"> | |
| <div class="w-6 h-6 rounded-full bg-gray-500"></div> | |
| <span class="text-sm font-bold">${u.username}</span> | |
| </div> | |
| <button class="text-[10px] border border-cyan-400 text-cyan-400 px-2 py-1 rounded hover:bg-cyan-400 hover:text-black"> | |
| CONNECT | |
| </button> | |
| `; | |
| // Logic for "Connect" -> Create DM channel would go here | |
| list.appendChild(div); | |
| }); | |
| } | |
| // --- 4. ADMIN & CHAT --- | |
| // (Admin Logic code from previous answer remains similar but linked to new modals) | |
| document.getElementById('admin-trigger').addEventListener('click', () => { | |
| document.getElementById('admin-modal').classList.add('open'); | |
| }); | |
| document.getElementById('admin-code').addEventListener('input', (e) => { | |
| if(e.target.value.length >= 4) { | |
| document.getElementById('admin-create-form').classList.remove('hidden'); | |
| } | |
| }); | |
| document.getElementById('admin-create-form').addEventListener('submit', async (e) => { | |
| e.preventDefault(); | |
| const code = document.getElementById('admin-code').value; | |
| const newU = document.getElementById('new-user').value; | |
| const newP = document.getElementById('new-pass').value; | |
| try { | |
| const res = await fetch('/api/admin/create_user', { | |
| method: 'POST', headers: {'Content-Type': 'application/json'}, | |
| body: JSON.stringify({admin_code: code, new_username: newU, new_password: newP}) | |
| }); | |
| if(!res.ok) throw new Error("DENIED"); | |
| const data = await res.json(); | |
| await supabase.from('users').insert({ | |
| username: data.username, | |
| password_hash: data.password_hash, | |
| badge: "BETA" | |
| }); | |
| alert("CREATED"); | |
| document.getElementById('admin-modal').classList.remove('open'); | |
| } catch(e) { alert("ACCESS DENIED"); } | |
| }); | |
| // --- 5. APP INIT & WEBSOCKET FIX --- | |
| function initApp() { | |
| const msgContainer = document.getElementById('messages-container'); | |
| // Load Chat History | |
| supabase.from('messages').select('*').eq('channel_id', 'general').order('created_at') | |
| .then(({data}) => { if(data) data.forEach(renderMessage); }); | |
| // Realtime Subscription | |
| supabase.channel('public:messages') | |
| .on('postgres_changes', { event: 'INSERT', schema: 'public', table: 'messages' }, payload => { | |
| if(payload.new.channel_id === 'general') renderMessage(payload.new); | |
| }) | |
| .subscribe(); | |
| // WebSocket for Voice/Signaling | |
| connectWebSocket(); | |
| } | |
| function connectWebSocket() { | |
| // Secure WebSocket handling for HF Spaces | |
| const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; | |
| // Remove trailing slash if present | |
| const host = window.location.host; | |
| const wsUrl = `${protocol}//${host}/ws/signal`; | |
| console.log("Attempting WS connect to:", wsUrl); | |
| ws = new WebSocket(wsUrl); | |
| ws.onopen = () => console.log("Signal Line Established"); | |
| ws.onerror = (e) => console.log("Signal Error (Ignore if not using Voice)", e); | |
| } | |
| const chatForm = document.getElementById('chat-form'); | |
| chatForm.addEventListener('submit', async (e) => { | |
| e.preventDefault(); | |
| const inp = document.getElementById('msg-input'); | |
| const text = inp.value.trim(); | |
| if(!text) return; | |
| inp.value = ''; | |
| const moodRes = await fetch('/api/analyze_mood', { | |
| method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify({text}) | |
| }); | |
| const mood = await moodRes.json(); | |
| await supabase.from('messages').insert({ | |
| content: text, user_id: currentUser.id, username: currentUser.username, | |
| mood_color: mood.mood_color, channel_id: 'general' | |
| }); | |
| }); | |
| function renderMessage(msg) { | |
| const div = document.createElement('div'); | |
| const isOwn = msg.username === currentUser.username; | |
| div.className = `flex gap-2 mb-4 ${isOwn ? 'flex-row-reverse' : ''} px-4`; | |
| div.innerHTML = ` | |
| <div class="bg-white/5 border border-white/5 p-3 rounded-xl max-w-[80%] text-sm" | |
| style="border-left: 3px solid ${msg.mood_color}"> | |
| <div class="text-[10px] text-white/40 mb-1">${msg.username}</div> | |
| ${msg.content} | |
| </div> | |
| `; | |
| const c = document.getElementById('messages-container'); | |
| c.appendChild(div); | |
| c.scrollTop = c.scrollHeight; | |
| } | |
| }); |