From e387e07cc6b4765ee10a912ded06bee7e1f9e0ed Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Mon, 16 Feb 2026 22:09:05 +0000 Subject: [PATCH] Version stable 01 --- api_v1_user.php | 26 ++++-- assets/js/main.js | 209 +++++++++++++++++++++++++++++++--------------- index.php | 52 +++++++++++- requests.log | 36 ++++++++ test_save.php | 7 ++ 5 files changed, 256 insertions(+), 74 deletions(-) create mode 100644 test_save.php diff --git a/api_v1_user.php b/api_v1_user.php index c7468e3..22db499 100644 --- a/api_v1_user.php +++ b/api_v1_user.php @@ -1,28 +1,42 @@ date('Y-m-d H:i:s'), + 'method' => $_SERVER['REQUEST_METHOD'], + 'post' => $_POST, + 'session' => $_SESSION +]; + if ($_SERVER['REQUEST_METHOD'] === 'POST') { $user = getCurrentUser(); if (!$user) { + $log['error'] = 'Unauthorized'; + file_put_contents('requests.log', json_encode($log) . "\n", FILE_APPEND); echo json_encode(['success' => false, 'error' => 'Unauthorized']); exit; } + $log['user_id'] = $user['id']; + $username = !empty($_POST['username']) ? $_POST['username'] : $user['username']; $avatar_url = isset($_POST['avatar_url']) ? $_POST['avatar_url'] : $user['avatar_url']; - $dnd_mode = isset($_POST['dnd_mode']) ? (int)$_POST['dnd_mode'] : (int)($user['dnd_mode'] ?? 0); - $sound_notifications = isset($_POST['sound_notifications']) ? (int)$_POST['sound_notifications'] : (int)($user['sound_notifications'] ?? 0); - $theme = !empty($_POST['theme']) ? $_POST['theme'] : (!empty($user['theme']) ? $user['theme'] : 'dark'); + $dnd_mode = isset($_POST['dnd_mode']) ? (int)$_POST['dnd_mode'] : 0; + $sound_notifications = isset($_POST['sound_notifications']) ? (int)$_POST['sound_notifications'] : 0; + $theme = !empty($_POST['theme']) ? $_POST['theme'] : $user['theme']; try { $stmt = db()->prepare("UPDATE users SET username = ?, avatar_url = ?, dnd_mode = ?, sound_notifications = ?, theme = ? WHERE id = ?"); - $stmt->execute([$username, $avatar_url, $dnd_mode, $sound_notifications, $theme, $user['id']]); + $success = $stmt->execute([$username, $avatar_url, $dnd_mode, $sound_notifications, $theme, $user['id']]); + $log['db_success'] = $success; + file_put_contents('requests.log', json_encode($log) . "\n", FILE_APPEND); echo json_encode(['success' => true]); } catch (Exception $e) { + $log['db_error'] = $e->getMessage(); + file_put_contents('requests.log', json_encode($log) . "\n", FILE_APPEND); echo json_encode(['success' => false, 'error' => $e->getMessage()]); } exit; diff --git a/assets/js/main.js b/assets/js/main.js index 42df985..a7b7f0f 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -121,9 +121,6 @@ document.addEventListener('DOMContentLoaded', () => { div.onclick = (e) => { if (e.target.closest('.emote-actions')) return; navigator.clipboard.writeText(emote.code); - const originalBg = div.style.backgroundColor; - div.style.backgroundColor = 'var(--blurple)'; - setTimeout(() => div.style.backgroundColor = originalBg, 200); }; div.querySelector('.delete-emote').onclick = async (e) => { @@ -163,9 +160,6 @@ document.addEventListener('DOMContentLoaded', () => { div.textContent = emoji; div.onclick = () => { navigator.clipboard.writeText(emoji); - const originalBg = div.style.backgroundColor; - div.style.backgroundColor = 'var(--blurple)'; - setTimeout(() => div.style.backgroundColor = originalBg, 200); }; grid.appendChild(div); }); @@ -179,10 +173,131 @@ document.addEventListener('DOMContentLoaded', () => { btn.style.backgroundColor = idx === 0 ? 'var(--separator)' : 'transparent'; btn.innerHTML = ` ${cat}`; - btn.onclick = () => { - const sidebar_btns = picker.querySelectorAll('.emoji-sidebar button'); - sidebar_btns.forEach(b => b.style.backgroundColor = 'transparent'); + btn.onclick = async () => { + sidebar.querySelectorAll('button').forEach(b => { + b.classList.remove('active'); + b.style.backgroundColor = 'transparent'; + }); + btn.classList.add('active'); btn.style.backgroundColor = 'var(--separator)'; + await renderGrid(cat); + }; + sidebar.appendChild(btn); + }); + + searchInput.oninput = async () => { + const term = searchInput.value.trim(); + if (term) { + sidebar.querySelectorAll('button').forEach(b => { + b.classList.remove('active'); + b.style.backgroundColor = 'transparent'; + }); + await renderGrid(null, term); + } else { + const activeBtn = sidebar.querySelector('button.active'); + const activeCat = activeBtn ? activeBtn.innerText.trim() : 'Custom'; + await renderGrid(activeCat); + } + }; + + if (uploadInput) { + uploadInput.onchange = async () => { + const file = uploadInput.files[0]; + if (!file) return; + const fd = new FormData(); + fd.append('emote', file); + const res = await (await fetch('api/emotes.php?action=upload', { method: 'POST', body: fd })).json(); + if (res.success) renderGrid('Custom'); + else alert(res.error || "Upload failed"); + }; + } + + await renderGrid('Custom'); + } + + const UniversalEmojiPicker = { + currentPicker: null, + show: async function(anchor, callback, options = {}) { + this.hide(); + const picker = document.createElement('div'); + picker.className = 'emoji-picker-container rounded shadow-lg p-0 d-flex flex-column'; + picker.style.position = 'fixed'; + picker.style.zIndex = '10000'; + picker.style.width = options.width || '400px'; + picker.style.height = options.height || '450px'; + picker.style.backgroundColor = '#2b2d31'; + picker.style.border = '1px solid #1e1f22'; + picker.style.display = 'flex'; + picker.style.flexDirection = 'column'; + + const mainLayout = document.createElement('div'); + mainLayout.className = 'd-flex flex-grow-1 overflow-hidden'; + + const tabs = document.createElement('div'); + tabs.className = 'emoji-sidebar d-flex flex-column p-2 border-end border-secondary custom-scrollbar'; + tabs.style.width = '60px'; + tabs.style.overflowY = 'auto'; + tabs.style.backgroundColor = '#1e1f22'; + + const contentArea = document.createElement('div'); + contentArea.className = 'd-flex flex-column flex-grow-1'; + + const searchContainer = document.createElement('div'); + searchContainer.className = 'p-2 border-bottom border-secondary'; + const searchInput = document.createElement('input'); + searchInput.type = 'text'; + searchInput.placeholder = 'Search emojis...'; + searchInput.className = 'form-control form-control-sm bg-dark border-secondary text-white'; + searchContainer.appendChild(searchInput); + + const grid = document.createElement('div'); + grid.className = 'emoji-grid flex-grow-1 p-2 overflow-auto custom-scrollbar'; + grid.style.display = 'grid'; + grid.style.gridTemplateColumns = 'repeat(auto-fill, minmax(36px, 1fr))'; + grid.style.gap = '4px'; + + const categories = ['Custom', ...Object.keys(EMOJI_CATEGORIES)]; + + const renderGrid = async (category, searchTerm = '') => { + grid.innerHTML = ''; + if (category === 'Custom' && !searchTerm) { + const emotes = await window.loadCustomEmotes(); + emotes.forEach(emote => { + const div = document.createElement('div'); + div.className = 'emoji-item rounded p-1 text-center'; + div.style.cursor = 'pointer'; + div.innerHTML = ``; + div.onclick = () => { + callback(emote.code); + if (!options.keepOpen) this.hide(); + }; + grid.appendChild(div); + }); + } else { + const list = searchTerm ? ALL_EMOJIS.filter(e => e.includes(searchTerm)) : EMOJI_CATEGORIES[category]; + (list || []).forEach(emoji => { + const div = document.createElement('div'); + div.className = 'emoji-item rounded p-1 text-center'; + div.style.cursor = 'pointer'; + div.style.fontSize = '20px'; + div.textContent = emoji; + div.onclick = () => { + callback(emoji); + if (!options.keepOpen) this.hide(); + }; + grid.appendChild(div); + }); + } + }; + + categories.forEach(cat => { + const btn = document.createElement('button'); + btn.className = 'btn btn-link text-white text-decoration-none p-2 mb-1 opacity-75'; + btn.title = cat; + btn.innerHTML = categoryIcons[cat] || '😀'; + btn.onclick = async () => { + tabs.querySelectorAll('button').forEach(b => b.classList.remove('active')); + btn.classList.add('active'); await renderGrid(cat); }; tabs.appendChild(btn); @@ -191,21 +306,20 @@ document.addEventListener('DOMContentLoaded', () => { searchInput.oninput = async () => { const term = searchInput.value.trim(); if (term) { - tabs.querySelectorAll('button').forEach(b => { - b.classList.remove('active'); - b.style.backgroundColor = 'transparent'; - }); + tabs.querySelectorAll('button').forEach(b => b.classList.remove('active')); await renderGrid(null, term); } else { const activeBtn = tabs.querySelector('button.active'); - const activeCat = activeBtn ? activeBtn.title : 'Custom'; - await renderGrid(activeCat); + await renderGrid(activeBtn ? activeBtn.title : 'Custom'); } }; - picker.appendChild(tabs); - picker.appendChild(searchContainer); - picker.appendChild(grid); + mainLayout.appendChild(tabs); + contentArea.appendChild(searchContainer); + contentArea.appendChild(grid); + mainLayout.appendChild(contentArea); + picker.appendChild(mainLayout); + document.body.appendChild(picker); this.currentPicker = picker; @@ -213,24 +327,14 @@ document.addEventListener('DOMContentLoaded', () => { const rect = anchor.getBoundingClientRect(); let top = rect.top - picker.offsetHeight - 10; if (top < 0) top = rect.bottom + 10; - let left = rect.left; if (left + picker.offsetWidth > window.innerWidth) left = window.innerWidth - picker.offsetWidth - 20; if (left < 10) left = 10; - - // Ensure it doesn't go off screen at the bottom - if (top + picker.offsetHeight > window.innerHeight) { - top = window.innerHeight - picker.offsetHeight - 10; - } - // Ensure it doesn't go off screen at the top - if (top < 0) top = 10; - picker.style.top = `${top}px`; picker.style.left = `${left}px`; await renderGrid('Custom'); - // Handle outside click const outsideClick = (e) => { if (!picker.contains(e.target) && e.target !== anchor && !anchor.contains(e.target)) { this.hide(); @@ -239,7 +343,6 @@ document.addEventListener('DOMContentLoaded', () => { }; setTimeout(() => document.addEventListener('click', outsideClick), 10); }, - hide() { if (this.currentPicker) { this.currentPicker.remove(); @@ -329,6 +432,13 @@ document.addEventListener('DOMContentLoaded', () => { window.loadCustomEmotes(); + const emotesTabBtn = document.getElementById('emotes-tab-btn'); + if (emotesTabBtn) { + emotesTabBtn.addEventListener('click', () => { + setupSettingsEmotes(); + }); + } + // Scroll to bottom scrollToBottom(true); @@ -2177,6 +2287,8 @@ document.addEventListener('DOMContentLoaded', () => { }); // User Settings - Avatar Search + // User Settings - Save logic removed and moved to index.php for reliability + const avatarSearchBtn = document.getElementById('search-avatar-btn'); const avatarSearchQuery = document.getElementById('avatar-search-query'); const avatarResults = document.getElementById('avatar-results'); @@ -2227,44 +2339,7 @@ document.addEventListener('DOMContentLoaded', () => { }); } - // User Settings - Save - const saveSettingsBtn = document.getElementById('save-settings-btn'); - saveSettingsBtn?.addEventListener('click', async () => { - const form = document.getElementById('user-settings-form'); - if (!form) return; - - const formData = new FormData(form); - - // Ensure switches are correctly sent as 1/0 - const dndMode = document.getElementById('dnd-switch')?.checked ? '1' : '0'; - const soundNotifications = document.getElementById('sound-switch')?.checked ? '1' : '0'; - formData.set('dnd_mode', dndMode); - formData.set('sound_notifications', soundNotifications); - - // Explicitly get theme to ensure it's captured - const themeInput = form.querySelector('input[name="theme"]:checked'); - const theme = themeInput ? themeInput.value : 'dark'; - formData.set('theme', theme); - - // Visual feedback - document.body.setAttribute('data-theme', theme); - - try { - const resp = await fetch('api_v1_user.php?v=' + Date.now(), { - method: 'POST', - body: formData - }); - const result = await resp.json(); - if (result.success) { - window.location.href = window.location.pathname + window.location.search; - } else { - alert(result.error || 'Failed to save settings'); - } - } catch (e) { - console.error('Error saving settings:', e); - alert('Failed to save settings. Please check your connection.'); - } - }); + // User Settings - Save handled in index.php function escapeHTML(str) { const div = document.createElement('div'); diff --git a/index.php b/index.php index 75495d1..38dde4d 100644 --- a/index.php +++ b/index.php @@ -1059,12 +1059,62 @@ $emote_html = '' . htmlspe
 
             <div class= - + + +