document.addEventListener('DOMContentLoaded', () => { // Basic interaction for toasts try { const toasts = document.querySelectorAll('.toast'); toasts.forEach(toastEl => { if (window.bootstrap && bootstrap.Toast) { const toast = new bootstrap.Toast(toastEl, { delay: 5000 }); toast.show(); } }); } catch (e) { console.error('Toast error:', e); } const html = document.documentElement; const updateIcons = (theme) => { // Update all theme toggle icons const icons = document.querySelectorAll('#theme-toggle i, #theme-toggle-mobile i'); icons.forEach(icon => { if (theme === 'dark') { icon.className = 'fa-solid fa-sun'; } else { icon.className = 'fa-solid fa-moon'; } }); // Update all theme status texts const textLabels = document.querySelectorAll('.theme-status-text'); textLabels.forEach(label => { label.textContent = theme === 'dark' ? 'Dark Mode' : 'Light Mode'; }); }; // Theme Toggle Logic const initThemeToggle = (btnId) => { const themeToggle = document.getElementById(btnId); if (!themeToggle) return; themeToggle.addEventListener('click', () => { const currentTheme = html.getAttribute('data-theme') || 'light'; const newTheme = currentTheme === 'dark' ? 'light' : 'dark'; // Update UI html.setAttribute('data-theme', newTheme); // Update All Icons and Labels updateIcons(newTheme); // Save preference document.cookie = `theme=${newTheme}; path=/; max-age=${365 * 24 * 60 * 60}`; localStorage.setItem('theme', newTheme); }); }; // AJAX Category Filtering const initCategoryAjax = () => { const filters = document.querySelectorAll('.category-filter'); const gridContainer = document.getElementById('apk-grid-container'); const dropdownBtn = document.getElementById('category-dropdown-btn'); const latestTitle = document.getElementById('latest-title'); if (!gridContainer || filters.length === 0) return; filters.forEach(filter => { filter.addEventListener('click', (e) => { e.preventDefault(); const category = filter.getAttribute('data-category'); const url = filter.getAttribute('href'); const categoryName = filter.textContent; // Update UI state gridContainer.style.opacity = '0.5'; gridContainer.style.pointerEvents = 'none'; // Fetch data fetch(url, { headers: { 'X-Requested-With': 'XMLHttpRequest' } }) .then(response => response.text()) .then(data => { gridContainer.innerHTML = data; gridContainer.style.opacity = '1'; gridContainer.style.pointerEvents = 'all'; // Update dropdown button text if (dropdownBtn) { dropdownBtn.innerHTML = `${categoryName} `; } // Update URL without refreshing window.history.pushState({ category: category }, '', url); }) .catch(err => { console.error('Fetch error:', err); gridContainer.style.opacity = '1'; gridContainer.style.pointerEvents = 'all'; }); }); }); // Handle browser back/forward window.addEventListener('popstate', (e) => { window.location.reload(); // Simple solution for now }); }; // AI Chat Assistant Logic const initAIChat = () => { const toggleBtn = document.getElementById('toggle-ai-chat'); const closeBtn = document.getElementById('close-ai-chat'); const chatWindow = document.getElementById('ai-chat-window'); const chatInput = document.getElementById('ai-chat-input'); const sendBtn = document.getElementById('send-ai-chat'); const messagesContainer = document.getElementById('ai-chat-messages'); if (!toggleBtn || !chatWindow) return; toggleBtn.addEventListener('click', () => { chatWindow.classList.toggle('d-none'); if (!chatWindow.classList.contains('d-none')) { chatInput.focus(); } }); closeBtn.addEventListener('click', () => { chatWindow.classList.add('d-none'); }); const appendMessage = (message, isUser = false) => { const div = document.createElement('div'); div.className = 'mb-3 d-flex ' + (isUser ? 'justify-content-end' : ''); const content = document.createElement('div'); content.className = (isUser ? 'bg-success text-white' : 'bg-white') + ' p-3 rounded-4 shadow-sm small'; content.style.maxWidth = '85%'; if (isUser) { content.style.borderBottomRightRadius = '0'; } else { content.style.borderBottomLeftRadius = '0'; } content.textContent = message; div.appendChild(content); messagesContainer.appendChild(div); messagesContainer.scrollTop = messagesContainer.scrollHeight; }; const sendMessage = () => { const message = chatInput.value.trim(); if (!message) return; appendMessage(message, true); chatInput.value = ''; // Loading state const loadingDiv = document.createElement('div'); loadingDiv.className = 'mb-3 d-flex'; loadingDiv.innerHTML = '
Thinking...
'; messagesContainer.appendChild(loadingDiv); messagesContainer.scrollTop = messagesContainer.scrollHeight; fetch('/api/ai/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ message: message }) }) .then(response => response.json()) .then(data => { messagesContainer.removeChild(loadingDiv); if (data.reply) { appendMessage(data.reply); } else { appendMessage(data.error || 'Sorry, something went wrong.'); } }) .catch(err => { messagesContainer.removeChild(loadingDiv); appendMessage('Error connecting to AI assistant.'); console.error(err); }); }; sendBtn.addEventListener('click', sendMessage); chatInput.addEventListener('keypress', (e) => { if (e.key === 'Enter') sendMessage(); }); }; // Initial Sync const currentTheme = html.getAttribute('data-theme') || 'light'; updateIcons(currentTheme); // Sync theme from localStorage if cookie is missing but localStorage has it const savedTheme = localStorage.getItem('theme'); const currentCookie = document.cookie.split('; ').find(row => row.startsWith('theme='))?.split('=')[1]; if (savedTheme && !currentCookie) { html.setAttribute('data-theme', savedTheme); updateIcons(savedTheme); document.cookie = `theme=${savedTheme}; path=/; max-age=${365 * 24 * 60 * 60}`; } initThemeToggle('theme-toggle'); initThemeToggle('theme-toggle-mobile'); initCategoryAjax(); initAIChat(); console.log('ApkNusa ready.'); });