feat(Nav): Refactor navigation structure to support nested items and improve visibility logic

feat(Radio):
-  Redesigned Queue modal, added drag & drop capabilities
- Added stream quality selector, currently offering: AAC @ 128kbps, AAC @ 320kbps & FLAC (lossless)

fix(middleware): Import API_URL from config and remove hardcoded API_URL definition

security(api): Enhance discord image and video caching with improved signature verification and error handling, updated image proxy to include production checks for signing secret
This commit is contained in:
2026-02-22 13:53:43 -05:00
parent b5bf5fd5a7
commit ef15b646cc
12 changed files with 1188 additions and 192 deletions

View File

@@ -30,6 +30,16 @@
}
};
function setLenisEnabled(enabled) {
const lenis = window.__lenis;
if (!lenis) return;
if (enabled) {
lenis.start();
} else {
lenis.stop();
}
}
function initMobileMenu() {
const menuBtn = document.getElementById("mobile-menu-btn");
const mobileMenu = document.getElementById("mobile-menu");
@@ -51,10 +61,12 @@
mobileMenu.classList.remove("open");
menuIcon.style.display = "block";
closeIcon.style.display = "none";
setLenisEnabled(true);
} else {
mobileMenu.classList.add("open");
menuIcon.style.display = "none";
closeIcon.style.display = "block";
setLenisEnabled(false);
}
});
@@ -64,6 +76,7 @@
mobileMenu.classList.remove("open");
menuIcon.style.display = "block";
closeIcon.style.display = "none";
setLenisEnabled(true);
});
});
@@ -73,6 +86,7 @@
mobileMenu.classList.remove("open");
menuIcon.style.display = "block";
closeIcon.style.display = "none";
setLenisEnabled(true);
}
}
};
@@ -81,7 +95,93 @@
document.addEventListener("click", closeHandler);
}
const ready = () => initMobileMenu();
function initDropdownExternalLinks() {
// Close desktop dropdown when external links are clicked
const dropdownLinks = document.querySelectorAll('.nav-dropdown a[target="_blank"]');
dropdownLinks.forEach((link) => {
link.addEventListener("click", () => {
// Blur the parent nav item to close the CSS hover-based dropdown
const navItem = link.closest('.nav-item--has-children');
if (navItem) {
const parentLink = navItem.querySelector(':scope > a');
if (parentLink) {
parentLink.blur();
}
// Force close by temporarily disabling pointer events
navItem.style.pointerEvents = 'none';
setTimeout(() => {
navItem.style.pointerEvents = '';
}, 100);
}
});
});
}
function initDesktopDropdownToggle() {
// Desktop: click parent to toggle dropdown open/closed
const desktopNav = document.querySelector('nav .hidden.md\\:flex');
if (!desktopNav) return;
const parentItems = desktopNav.querySelectorAll('.nav-item--has-children');
parentItems.forEach((navItem) => {
const parentLink = navItem.querySelector(':scope > a');
if (!parentLink) return;
parentLink.addEventListener('click', (e) => {
e.preventDefault();
const isOpen = navItem.classList.contains('dropdown-open');
// Close any other open dropdowns
parentItems.forEach((item) => item.classList.remove('dropdown-open'));
if (!isOpen) {
navItem.classList.add('dropdown-open');
}
});
});
// Close dropdown when clicking outside
document.addEventListener('click', (e) => {
if (!desktopNav.contains(e.target)) {
parentItems.forEach((item) => item.classList.remove('dropdown-open'));
}
});
}
function initMobileSubmenus() {
const mobileMenu = document.getElementById("mobile-menu");
if (!mobileMenu) return;
// Find all parent links that have a sibling mobile-subnav
const parentLinks = mobileMenu.querySelectorAll('a:has(+ .mobile-subnav)');
parentLinks.forEach((link) => {
const subnav = link.nextElementSibling;
const caret = link.querySelector('.mobile-caret');
if (!subnav || !subnav.classList.contains('mobile-subnav')) return;
// Clone to remove existing listeners
const newLink = link.cloneNode(true);
link.parentNode.replaceChild(newLink, link);
const newCaret = newLink.querySelector('.mobile-caret');
newLink.addEventListener('click', (e) => {
// Toggle subnav open/closed on click
e.preventDefault();
subnav.classList.toggle('open');
if (newCaret) newCaret.classList.toggle('open');
});
});
}
const ready = () => {
initMobileMenu();
initDropdownExternalLinks();
initDesktopDropdownToggle();
initMobileSubmenus();
};
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", ready, { once: true });
@@ -89,5 +189,10 @@
ready();
}
document.addEventListener("astro:page-load", initMobileMenu);
document.addEventListener("astro:page-load", () => {
initMobileMenu();
initDropdownExternalLinks();
initDesktopDropdownToggle();
initMobileSubmenus();
});
})();