better titlebar and fixes
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
;(() => {
|
||||
const token = localStorage.getItem("token")
|
||||
|
||||
if (!token && window.location.pathname !== "/") {
|
||||
window.location.href = "/"
|
||||
}
|
||||
@@ -12,41 +11,61 @@ async function loadMeUI() {
|
||||
|
||||
try {
|
||||
const res = await fetch("/api/me", {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
headers: { Authorization: `Bearer ${token}` },
|
||||
})
|
||||
|
||||
if (!res.ok) return
|
||||
|
||||
const user = await res.json()
|
||||
const avatarUrl = user.avatar || "/public/assets/avatar.png"
|
||||
|
||||
const navUser = document.getElementById("nav-user")
|
||||
const navUsername = document.getElementById("nav-username")
|
||||
const navAvatar = document.getElementById("nav-avatar")
|
||||
const dropdownAvatar = document.getElementById("dropdown-avatar")
|
||||
|
||||
if (!navUser || !navUsername || !navAvatar) return
|
||||
if (navUser && navUsername && navAvatar) {
|
||||
navUser.style.display = "flex"
|
||||
navUsername.textContent = user.username
|
||||
navAvatar.src = avatarUrl
|
||||
if (dropdownAvatar) dropdownAvatar.src = avatarUrl
|
||||
}
|
||||
|
||||
navUser.style.display = "flex"
|
||||
navUsername.textContent = user.username
|
||||
const titlebarUserBox = document.getElementById("titlebar-user-box")
|
||||
const titlebarUsername = document.getElementById("titlebar-username")
|
||||
const titlebarAvatar = document.getElementById("titlebar-avatar")
|
||||
const titlebarDropdownAvatar = document.getElementById("titlebar-dropdown-avatar")
|
||||
const titlebarDropdownUsername = document.getElementById("titlebar-dropdown-username")
|
||||
|
||||
const avatarUrl = user.avatar || "/public/assets/avatar.png"
|
||||
navAvatar.src = avatarUrl
|
||||
if (dropdownAvatar) {
|
||||
dropdownAvatar.src = avatarUrl
|
||||
if (titlebarUserBox && titlebarUsername && titlebarAvatar) {
|
||||
titlebarUserBox.style.display = "flex"
|
||||
|
||||
titlebarUsername.textContent = user.username
|
||||
titlebarAvatar.src = avatarUrl
|
||||
|
||||
if (titlebarDropdownAvatar) titlebarDropdownAvatar.src = avatarUrl
|
||||
if (titlebarDropdownUsername) titlebarDropdownUsername.textContent = user.username
|
||||
}
|
||||
|
||||
setupDropdown()
|
||||
|
||||
} catch (e) {
|
||||
console.error("Failed to load user UI:", e)
|
||||
}
|
||||
}
|
||||
|
||||
// Variable para saber si el modal ya fue cargado
|
||||
let settingsModalLoaded = false;
|
||||
|
||||
document.getElementById('nav-settings').addEventListener('click', openSettings)
|
||||
const navSettingsBtn = document.getElementById('nav-settings');
|
||||
const titlebarSettingsBtn = document.getElementById('titlebar-settings');
|
||||
|
||||
if (navSettingsBtn) navSettingsBtn.addEventListener('click', openSettings);
|
||||
if (titlebarSettingsBtn) titlebarSettingsBtn.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
openSettings();
|
||||
document.getElementById("titlebar-dropdown")?.classList.remove("active");
|
||||
});
|
||||
|
||||
async function openSettings() {
|
||||
if (!settingsModalLoaded) {
|
||||
@@ -55,63 +74,69 @@ async function openSettings() {
|
||||
const html = await res.text()
|
||||
document.body.insertAdjacentHTML('beforeend', html)
|
||||
settingsModalLoaded = true;
|
||||
|
||||
// Esperar un momento para que el DOM se actualice
|
||||
await new Promise(resolve => setTimeout(resolve, 50));
|
||||
|
||||
// Ahora cargar los settings
|
||||
if (window.toggleSettingsModal) {
|
||||
await window.toggleSettingsModal(false);
|
||||
}
|
||||
if (window.toggleSettingsModal) await window.toggleSettingsModal(false);
|
||||
} catch (err) {
|
||||
console.error('Error loading settings modal:', err);
|
||||
}
|
||||
} else {
|
||||
if (window.toggleSettingsModal) {
|
||||
await window.toggleSettingsModal(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function closeSettings() {
|
||||
const modal = document.getElementById('settings-modal');
|
||||
if (modal) {
|
||||
modal.classList.add('hidden');
|
||||
if (window.toggleSettingsModal) await window.toggleSettingsModal(false);
|
||||
}
|
||||
}
|
||||
|
||||
function setupDropdown() {
|
||||
|
||||
const userAvatarBtn = document.querySelector(".user-avatar-btn")
|
||||
const navDropdown = document.getElementById("nav-dropdown")
|
||||
const navLogout = document.getElementById("nav-logout")
|
||||
|
||||
if (!userAvatarBtn || !navDropdown || !navLogout) return
|
||||
if (userAvatarBtn && navDropdown) {
|
||||
userAvatarBtn.addEventListener("click", (e) => {
|
||||
e.stopPropagation()
|
||||
navDropdown.classList.toggle("active")
|
||||
|
||||
userAvatarBtn.addEventListener("click", (e) => {
|
||||
e.stopPropagation()
|
||||
navDropdown.classList.toggle("active")
|
||||
})
|
||||
document.getElementById("titlebar-dropdown")?.classList.remove("active")
|
||||
})
|
||||
if (navLogout) {
|
||||
navLogout.addEventListener("click", () => {
|
||||
localStorage.removeItem("token")
|
||||
window.location.href = "/"
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const titlebarUserBox = document.getElementById("titlebar-user-box")
|
||||
const titlebarDropdown = document.getElementById("titlebar-dropdown")
|
||||
const titlebarLogout = document.getElementById("titlebar-logout")
|
||||
|
||||
if (titlebarUserBox && titlebarDropdown) {
|
||||
titlebarUserBox.addEventListener("click", (e) => {
|
||||
e.stopPropagation()
|
||||
titlebarDropdown.classList.toggle("active")
|
||||
|
||||
document.getElementById("nav-dropdown")?.classList.remove("active")
|
||||
})
|
||||
|
||||
if (titlebarLogout) {
|
||||
titlebarLogout.addEventListener("click", (e) => {
|
||||
e.stopPropagation()
|
||||
localStorage.removeItem("token")
|
||||
window.location.href = "/"
|
||||
})
|
||||
}
|
||||
|
||||
titlebarDropdown.addEventListener("click", (e) => {
|
||||
e.stopPropagation()
|
||||
})
|
||||
}
|
||||
|
||||
document.addEventListener("click", (e) => {
|
||||
if (!navDropdown.contains(e.target)) {
|
||||
if (navDropdown && !navDropdown.contains(e.target)) {
|
||||
navDropdown.classList.remove("active")
|
||||
}
|
||||
})
|
||||
|
||||
navDropdown.addEventListener("click", (e) => {
|
||||
e.stopPropagation()
|
||||
})
|
||||
|
||||
navLogout.addEventListener("click", () => {
|
||||
localStorage.removeItem("token")
|
||||
window.location.href = "/"
|
||||
})
|
||||
|
||||
const dropdownLinks = navDropdown.querySelectorAll("a.dropdown-item")
|
||||
dropdownLinks.forEach((link) => {
|
||||
link.addEventListener("click", () => {
|
||||
navDropdown.classList.remove("active")
|
||||
})
|
||||
if (titlebarDropdown && !titlebarDropdown.contains(e.target)) {
|
||||
titlebarDropdown.classList.remove("active")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -159,7 +184,6 @@ searchWrapper.addEventListener('click', (e) => {
|
||||
}
|
||||
});
|
||||
|
||||
// Cerrar el buscador si se hace clic fuera
|
||||
document.addEventListener('click', (e) => {
|
||||
if (!searchWrapper.contains(e.target)) {
|
||||
searchWrapper.classList.remove('active-mobile');
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const reader = document.getElementById('reader');
|
||||
const panel = document.getElementById('settings-panel');
|
||||
const overlay = document.getElementById('overlay');
|
||||
const overlay2 = document.getElementById('overlay');
|
||||
const settingsBtn = document.getElementById('settings-btn');
|
||||
const closePanel = document.getElementById('close-panel');
|
||||
const chapterLabel = document.getElementById('chapter-label');
|
||||
@@ -665,14 +665,14 @@ document.getElementById('back-btn').addEventListener('click', () => {
|
||||
// Panel de configuración
|
||||
settingsBtn.addEventListener('click', () => {
|
||||
panel.classList.add('open');
|
||||
overlay.classList.add('active');
|
||||
overlay2.classList.add('active');
|
||||
});
|
||||
closePanel.addEventListener('click', closeSettings);
|
||||
overlay.addEventListener('click', closeSettings);
|
||||
overlay2.addEventListener('click', closeSettings);
|
||||
|
||||
function closeSettings() {
|
||||
panel.classList.remove('open');
|
||||
overlay.classList.remove('active');
|
||||
overlay2.classList.remove('active');
|
||||
}
|
||||
document.addEventListener('keydown', (e) => {
|
||||
if (e.key === 'Escape' && panel.classList.contains('open')) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const providerSelector = document.getElementById('provider-selector');
|
||||
const searchInput = document.getElementById('search-input');
|
||||
const searchInput2 = document.getElementById('search-input');
|
||||
const resultsContainer = document.getElementById('gallery-results');
|
||||
|
||||
let currentPage = 1;
|
||||
@@ -231,7 +231,7 @@ function showSkeletons(count, append = false) {
|
||||
async function searchGallery(isLoadMore = false) {
|
||||
if (isLoading) return;
|
||||
|
||||
const query = searchInput.value.trim();
|
||||
const query = searchInput2.value.trim();
|
||||
const provider = providerSelector.value;
|
||||
const page = isLoadMore ? currentPage + 1 : 1;
|
||||
|
||||
@@ -351,22 +351,22 @@ async function loadExtensions() {
|
||||
|
||||
providerSelector.addEventListener('change', () => {
|
||||
if (providerSelector.value === 'favorites') {
|
||||
searchInput.placeholder = "Search in favorites...";
|
||||
searchInput2.placeholder = "Search in favorites...";
|
||||
} else {
|
||||
searchInput.placeholder = "Search in gallery...";
|
||||
searchInput2.placeholder = "Search in gallery...";
|
||||
}
|
||||
searchGallery(false);
|
||||
});
|
||||
|
||||
let searchTimeout;
|
||||
|
||||
searchInput.addEventListener('input', () => {
|
||||
searchInput2.addEventListener('input', () => {
|
||||
clearTimeout(searchTimeout);
|
||||
searchTimeout = setTimeout(() => {
|
||||
searchGallery(false);
|
||||
}, 500);
|
||||
});
|
||||
searchInput.addEventListener('keydown', e => {
|
||||
searchInput2.addEventListener('keydown', e => {
|
||||
if (e.key === 'Enter') {
|
||||
clearTimeout(searchTimeout);
|
||||
searchGallery(false);
|
||||
|
||||
@@ -1,7 +1,18 @@
|
||||
import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
|
||||
import {FastifyInstance, FastifyReply, FastifyRequest} from 'fastify';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
let cachedTitlebar: string | null = null;
|
||||
|
||||
function getTitlebarHTML(): string {
|
||||
if (!cachedTitlebar) {
|
||||
|
||||
const titlebarPath = path.join(__dirname, '..', '..', 'views', 'components', 'titlebar.html');
|
||||
cachedTitlebar = fs.readFileSync(titlebarPath, 'utf-8');
|
||||
}
|
||||
return cachedTitlebar;
|
||||
}
|
||||
|
||||
let cachedNavbar: string | null = null;
|
||||
|
||||
function getNavbarHTML(activePage: string, showSearch: boolean = true): string {
|
||||
@@ -14,6 +25,7 @@ function getNavbarHTML(activePage: string, showSearch: boolean = true): string {
|
||||
|
||||
const pages = ['anime', 'books', 'gallery', 'schedule' , 'marketplace'];
|
||||
pages.forEach(page => {
|
||||
|
||||
const regex = new RegExp(`(<button class="nav-button[^"]*)"\\s+data-page="${page}"`, 'g');
|
||||
if (page === activePage) {
|
||||
navbar = navbar.replace(regex, `$1 active" data-page="${page}"`);
|
||||
@@ -30,10 +42,15 @@ function getNavbarHTML(activePage: string, showSearch: boolean = true): string {
|
||||
return navbar;
|
||||
}
|
||||
|
||||
function injectNavbar(htmlContent: string, activePage: string, showSearch: boolean = true): string {
|
||||
const navbar = getNavbarHTML(activePage, showSearch);
|
||||
function injectLayout(htmlContent: string, activePage: string | null = null, showSearch: boolean = true): string {
|
||||
let contentToInject = getTitlebarHTML();
|
||||
|
||||
return htmlContent.replace(/<body[^>]*>/, `$&\n${navbar}`);
|
||||
if (activePage !== null) {
|
||||
const navbar = getNavbarHTML(activePage, showSearch);
|
||||
contentToInject += `\n${navbar}`;
|
||||
}
|
||||
|
||||
return htmlContent.replace(/<body[^>]*>/, `$&\n${contentToInject}`);
|
||||
}
|
||||
|
||||
async function viewsRoutes(fastify: FastifyInstance) {
|
||||
@@ -41,105 +58,116 @@ async function viewsRoutes(fastify: FastifyInstance) {
|
||||
fastify.get('/', (req: FastifyRequest, reply: FastifyReply) => {
|
||||
const htmlPath = path.join(__dirname, '..', '..', 'views', 'users.html');
|
||||
const html = fs.readFileSync(htmlPath, 'utf-8');
|
||||
reply.type('text/html').send(html);
|
||||
|
||||
const finalHtml = injectLayout(html, null);
|
||||
reply.type('text/html').send(finalHtml);
|
||||
});
|
||||
|
||||
fastify.get('/anime', (req: FastifyRequest, reply: FastifyReply) => {
|
||||
const htmlPath = path.join(__dirname, '..', '..', 'views', 'anime', 'animes.html');
|
||||
const html = fs.readFileSync(htmlPath, 'utf-8');
|
||||
const htmlWithNavbar = injectNavbar(html, 'anime', true);
|
||||
reply.type('text/html').send(htmlWithNavbar);
|
||||
const finalHtml = injectLayout(html, 'anime', true);
|
||||
reply.type('text/html').send(finalHtml);
|
||||
});
|
||||
|
||||
fastify.get('/profile', (req: FastifyRequest, reply: FastifyReply) => {
|
||||
const htmlPath = path.join(__dirname, '..', '..', 'views', 'profile.html');
|
||||
const html = fs.readFileSync(htmlPath, 'utf-8');
|
||||
const htmlWithNavbar = injectNavbar(html, '', false);
|
||||
reply.type('text/html').send(htmlWithNavbar);
|
||||
const finalHtml = injectLayout(html, '', false);
|
||||
reply.type('text/html').send(finalHtml);
|
||||
});
|
||||
|
||||
fastify.get('/books', (req: FastifyRequest, reply: FastifyReply) => {
|
||||
const htmlPath = path.join(__dirname, '..', '..', 'views', 'books', 'books.html');
|
||||
const html = fs.readFileSync(htmlPath, 'utf-8');
|
||||
const htmlWithNavbar = injectNavbar(html, 'books', true);
|
||||
reply.type('text/html').send(htmlWithNavbar);
|
||||
const finalHtml = injectLayout(html, 'books', true);
|
||||
reply.type('text/html').send(finalHtml);
|
||||
});
|
||||
|
||||
fastify.get('/schedule', (req: FastifyRequest, reply: FastifyReply) => {
|
||||
const htmlPath = path.join(__dirname, '..', '..', 'views', 'schedule.html');
|
||||
const html = fs.readFileSync(htmlPath, 'utf-8');
|
||||
const htmlWithNavbar = injectNavbar(html, 'schedule', false);
|
||||
reply.type('text/html').send(htmlWithNavbar);
|
||||
const finalHtml = injectLayout(html, 'schedule', false);
|
||||
reply.type('text/html').send(finalHtml);
|
||||
});
|
||||
|
||||
fastify.get('/gallery', (req: FastifyRequest, reply: FastifyReply) => {
|
||||
const htmlPath = path.join(__dirname, '..', '..', 'views', 'gallery', 'gallery.html');
|
||||
const html = fs.readFileSync(htmlPath, 'utf-8');
|
||||
const htmlWithNavbar = injectNavbar(html, 'gallery', true);
|
||||
reply.type('text/html').send(htmlWithNavbar);
|
||||
const finalHtml = injectLayout(html, 'gallery', true);
|
||||
reply.type('text/html').send(finalHtml);
|
||||
});
|
||||
|
||||
fastify.get('/marketplace', (req: FastifyRequest, reply: FastifyReply) => {
|
||||
const htmlPath = path.join(__dirname, '..', '..', 'views', 'marketplace.html');
|
||||
const html = fs.readFileSync(htmlPath, 'utf-8');
|
||||
const htmlWithNavbar = injectNavbar(html, 'marketplace', false);
|
||||
reply.type('text/html').send(htmlWithNavbar);
|
||||
const finalHtml = injectLayout(html, 'marketplace', false);
|
||||
reply.type('text/html').send(finalHtml);
|
||||
});
|
||||
|
||||
fastify.get('/gallery/:extension/*', (req: FastifyRequest, reply: FastifyReply) => {
|
||||
const htmlPath = path.join(__dirname, '..', '..', 'views', 'gallery', 'image.html');
|
||||
const html = fs.readFileSync(htmlPath, 'utf-8');
|
||||
const htmlWithNavbar = injectNavbar(html, 'gallery', true);
|
||||
reply.type('text/html').send(htmlWithNavbar);
|
||||
const finalHtml = injectLayout(html, 'gallery', true);
|
||||
reply.type('text/html').send(finalHtml);
|
||||
});
|
||||
|
||||
fastify.get('/gallery/favorites/*', (req: FastifyRequest, reply: FastifyReply) => {
|
||||
const htmlPath = path.join(__dirname, '..', '..', 'views', 'gallery', 'image.html');
|
||||
const html = fs.readFileSync(htmlPath, 'utf-8');
|
||||
const htmlWithNavbar = injectNavbar(html, 'gallery', true);
|
||||
reply.type('text/html').send(htmlWithNavbar);
|
||||
const finalHtml = injectLayout(html, 'gallery', true);
|
||||
reply.type('text/html').send(finalHtml);
|
||||
});
|
||||
|
||||
fastify.get('/anime/:id', (req: FastifyRequest, reply: FastifyReply) => {
|
||||
const htmlPath = path.join(__dirname, '..', '..', 'views', 'anime', 'anime.html');
|
||||
const html = fs.readFileSync(htmlPath, 'utf-8');
|
||||
reply.type('text/html').send(html);
|
||||
|
||||
const finalHtml = injectLayout(html, null);
|
||||
reply.type('text/html').send(finalHtml);
|
||||
});
|
||||
|
||||
fastify.get('/anime/:extension/*', (req: FastifyRequest, reply: FastifyReply) => {
|
||||
const htmlPath = path.join(__dirname, '..', '..', 'views', 'anime', 'anime.html');
|
||||
const html = fs.readFileSync(htmlPath, 'utf-8');
|
||||
reply.type('text/html').send(html);
|
||||
const finalHtml = injectLayout(html, null);
|
||||
reply.type('text/html').send(finalHtml);
|
||||
});
|
||||
|
||||
fastify.get('/book/:id', (req: FastifyRequest, reply: FastifyReply) => {
|
||||
const htmlPath = path.join(__dirname, '..', '..', 'views', 'books', 'book.html');
|
||||
const html = fs.readFileSync(htmlPath, 'utf-8');
|
||||
reply.type('text/html').send(html);
|
||||
const finalHtml = injectLayout(html, null);
|
||||
reply.type('text/html').send(finalHtml);
|
||||
});
|
||||
|
||||
fastify.get('/book/:extension/*', (req: FastifyRequest, reply: FastifyReply) => {
|
||||
const htmlPath = path.join(__dirname, '..', '..', 'views', 'books', 'book.html');
|
||||
const html = fs.readFileSync(htmlPath, 'utf-8');
|
||||
reply.type('text/html').send(html);
|
||||
const finalHtml = injectLayout(html, null);
|
||||
reply.type('text/html').send(finalHtml);
|
||||
});
|
||||
|
||||
fastify.get('/read/:provider/:chapter/*', (req: FastifyRequest, reply: FastifyReply) => {
|
||||
const htmlPath = path.join(__dirname, '..', '..', 'views', 'books', 'read.html');
|
||||
const html = fs.readFileSync(htmlPath, 'utf-8');
|
||||
reply.type('text/html').send(html);
|
||||
|
||||
const finalHtml = injectLayout(html, null);
|
||||
reply.type('text/html').send(finalHtml);
|
||||
});
|
||||
|
||||
fastify.get('/room', (req: FastifyRequest, reply: FastifyReply) => {
|
||||
const htmlPath = path.join(__dirname, '..', '..', 'views', 'room.html');
|
||||
const html = fs.readFileSync(htmlPath, 'utf-8');
|
||||
reply.type('text/html').send(html);
|
||||
const finalHtml = injectLayout(html, null);
|
||||
reply.type('text/html').send(finalHtml);
|
||||
});
|
||||
|
||||
fastify.setNotFoundHandler((req, reply) => {
|
||||
const htmlPath = path.join(__dirname, '..', '..', 'views', '404.html');
|
||||
const html = fs.readFileSync(htmlPath, 'utf-8');
|
||||
reply.code(404).type('text/html').send(html);
|
||||
const finalHtml = injectLayout(html, null);
|
||||
reply.code(404).type('text/html').send(finalHtml);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user