121 lines
3.5 KiB
JavaScript
121 lines
3.5 KiB
JavaScript
(function () {
|
|
// =========== README ==========
|
|
// Update the message below
|
|
// incrementversion DISMISSED_KEY
|
|
const DISMISSED_KEY = 'jf-banner-dismissed-v1';
|
|
const BANNER_ID = 'custom-announcement-banner';
|
|
|
|
// Edit these to change banner content:
|
|
const BANNER_HTML = `
|
|
<span class="banner-text">
|
|
<strong>News:</strong>
|
|
<a href="https://jellyfin.org/downloads/clients/" target="_blank" rel="noopener">
|
|
Get the official app for your device
|
|
</a>
|
|
|
|
|
<a href="https://google.com/" target="_blank" rel="noopener">
|
|
Submit requests
|
|
</a>
|
|
</span>
|
|
<button id="custom-banner-close" aria-label="Dismiss banner">✕</button>
|
|
`;
|
|
|
|
const BANNER_CSS = `
|
|
#custom-announcement-banner {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
background: #1c3a5c;
|
|
color: #e0e0e0;
|
|
font-size: 0.9em;
|
|
padding: 8px 16px;
|
|
box-sizing: border-box;
|
|
width: 100%;
|
|
z-index: 9999;
|
|
border-bottom: 1px solid #2d5a8e;
|
|
}
|
|
#custom-announcement-banner a {
|
|
color: #7cb8f0;
|
|
}
|
|
#custom-banner-close {
|
|
background: none;
|
|
border: none;
|
|
color: #e0e0e0;
|
|
cursor: pointer;
|
|
font-size: 1.1em;
|
|
padding: 0 4px;
|
|
flex-shrink: 0;
|
|
}
|
|
`;
|
|
|
|
function applyOffset() {
|
|
const banner = document.getElementById(BANNER_ID);
|
|
const offset = banner ? (banner.offsetHeight + 'px') : '';
|
|
const header = document.querySelector('.headerTop');
|
|
const drawer = document.querySelector('.mainDrawer');
|
|
const sections = document.querySelector('.homeSectionsContainer');
|
|
if (header) header.style.marginTop = offset;
|
|
if (drawer) drawer.style.marginTop = offset;
|
|
if (sections) sections.style.marginTop = offset;
|
|
}
|
|
|
|
function isHomePage() {
|
|
const h = window.location.hash;
|
|
return h === '' || h === '#' || h === '#/home' || h === '#!'
|
|
|| h === '#!/' || h.startsWith('#/home') || h.startsWith('#!/home');
|
|
}
|
|
|
|
function injectBanner() {
|
|
if (document.getElementById(BANNER_ID)) return;
|
|
|
|
const banner = document.createElement('div');
|
|
banner.id = BANNER_ID;
|
|
banner.innerHTML = BANNER_HTML;
|
|
document.body.insertBefore(banner, document.body.firstChild);
|
|
|
|
document.getElementById('custom-banner-close').addEventListener('click', function () {
|
|
sessionStorage.setItem(DISMISSED_KEY, '1');
|
|
document.getElementById(BANNER_ID).remove();
|
|
applyOffset();
|
|
});
|
|
|
|
applyOffset();
|
|
}
|
|
|
|
function removeBanner() {
|
|
const el = document.getElementById(BANNER_ID);
|
|
if (el) el.remove();
|
|
applyOffset();
|
|
}
|
|
|
|
function update() {
|
|
if (sessionStorage.getItem(DISMISSED_KEY)) { removeBanner(); return; }
|
|
if (isHomePage()) { injectBanner(); } else { removeBanner(); }
|
|
}
|
|
|
|
// Inject CSS once
|
|
const style = document.createElement('style');
|
|
style.textContent = BANNER_CSS;
|
|
document.head.appendChild(style);
|
|
|
|
// React to SPA navigation
|
|
window.addEventListener('hashchange', update);
|
|
|
|
// Watch for Jellyfin's deferred body rendering (SPA bootstraps after DOMContentLoaded)
|
|
// subtree: true catches .headerTop wherever it appears in the DOM
|
|
const observer = new MutationObserver(function () {
|
|
update();
|
|
applyOffset();
|
|
});
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
observer.observe(document.body, { childList: true, subtree: true });
|
|
update();
|
|
});
|
|
|
|
// Fallback: run immediately if DOMContentLoaded already fired
|
|
if (document.readyState !== 'loading') update();
|
|
})();
|