Add client-side toggle option for seasonal settings
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 54s
All checks were successful
Auto Release Plugin / build-and-release (push) Successful in 54s
This commit is contained in:
@@ -74,7 +74,7 @@ function determineCurrentTheme() {
|
||||
const day = date.getDate(); // 1-31
|
||||
|
||||
if ((month === 11 && day >= 28) || (month === 0 && day <= 5)) return 'fireworks'; //new year fireworks december 28 - january 5
|
||||
|
||||
|
||||
if (month === 1 && day >= 10 && day <= 18) return 'hearts'; // valentine's day february 10 - 18
|
||||
|
||||
if (month === 11 && day >= 22 && day <= 27) return 'santa'; // christmas december 22 - 27
|
||||
@@ -83,9 +83,9 @@ function determineCurrentTheme() {
|
||||
if (month === 11) return 'snowflakes'; // snowflakes december
|
||||
if (month === 0 || month === 1) return 'snowfall'; // snow january, february
|
||||
// if (month === 0 || month === 1) return 'snowstorm'; // snow january, february
|
||||
|
||||
|
||||
if ((month === 2 && day >= 25) || (month === 3 && day <= 25)) return 'easter'; // easter march 25 - april 25
|
||||
|
||||
|
||||
//NOT IMPLEMENTED YET
|
||||
//if (month >= 2 && month <= 4) return 'spring'; // spring march, april, may
|
||||
|
||||
@@ -99,12 +99,21 @@ function determineCurrentTheme() {
|
||||
return 'none'; // Fallback (nothing)
|
||||
}
|
||||
|
||||
// helper to resolve paths for local testing vs production
|
||||
function resolvePath(path) {
|
||||
if (window.location.protocol === 'file:' || window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
|
||||
return path.replace('/Seasonals/Resources/', './');
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
// load theme csss
|
||||
function loadThemeCSS(cssPath) {
|
||||
if (!cssPath) return;
|
||||
|
||||
const link = document.createElement('link');
|
||||
link.rel = 'stylesheet';
|
||||
// link.href = resolvePath(cssPath);
|
||||
link.href = cssPath;
|
||||
|
||||
link.onerror = () => {
|
||||
@@ -121,6 +130,7 @@ function loadThemeJS(jsPath) {
|
||||
|
||||
const script = document.createElement('script');
|
||||
script.src = jsPath;
|
||||
// script.src = resolvePath(jsPath);
|
||||
script.defer = true;
|
||||
|
||||
script.onerror = () => {
|
||||
@@ -140,19 +150,29 @@ function updateThemeContainer(containerClass) {
|
||||
container.className = 'seasonals-container';
|
||||
document.body.appendChild(container);
|
||||
}
|
||||
|
||||
|
||||
container.className = `seasonals-container ${containerClass}`;
|
||||
console.log(`Seasonals-Container class updated to "${containerClass}".`);
|
||||
}
|
||||
|
||||
function removeSelf() {
|
||||
const script = document.currentScript;
|
||||
if (script) script.parentNode.removeChild(script);
|
||||
console.log('External script removed:', script);
|
||||
}
|
||||
// function removeSelf() {
|
||||
// const script = document.currentScript;
|
||||
// if (script) script.parentNode.removeChild(script);
|
||||
// console.log('External script removed:', script);
|
||||
// }
|
||||
|
||||
// initialize theme
|
||||
async function initializeTheme() {
|
||||
|
||||
// Check local user preference
|
||||
const isEnabled = getSavedSetting('seasonals-enabled', 'true') === 'true';
|
||||
if (!isEnabled) {
|
||||
console.log('Seasonals disabled by user preference.');
|
||||
return;
|
||||
}
|
||||
|
||||
const forcedTheme = getSavedSetting('seasonals-theme', 'auto');
|
||||
|
||||
let automateThemeSelection = true;
|
||||
let defaultTheme = 'none';
|
||||
|
||||
@@ -172,7 +192,11 @@ async function initializeTheme() {
|
||||
}
|
||||
|
||||
let currentTheme;
|
||||
if (automateThemeSelection === false) {
|
||||
|
||||
if (forcedTheme !== 'auto') {
|
||||
currentTheme = forcedTheme;
|
||||
console.log(`User forced theme: ${currentTheme}`);
|
||||
} else if (automateThemeSelection === false) {
|
||||
currentTheme = defaultTheme;
|
||||
} else {
|
||||
currentTheme = determineCurrentTheme();
|
||||
@@ -182,12 +206,11 @@ async function initializeTheme() {
|
||||
|
||||
if (!currentTheme || currentTheme === 'none') {
|
||||
console.log('No theme selected.');
|
||||
removeSelf();
|
||||
return;
|
||||
}
|
||||
|
||||
const theme = themeConfigs[currentTheme];
|
||||
|
||||
|
||||
if (!theme) {
|
||||
console.error(`Theme "${currentTheme}" not found.`);
|
||||
return;
|
||||
@@ -198,9 +221,168 @@ async function initializeTheme() {
|
||||
if (theme.js) loadThemeJS(theme.js);
|
||||
|
||||
console.log(`Theme "${currentTheme}" loaded.`);
|
||||
|
||||
removeSelf();
|
||||
}
|
||||
|
||||
|
||||
initializeTheme();
|
||||
|
||||
|
||||
// User UI Seasonal Settings
|
||||
|
||||
function getSavedSetting(key, defaultValue) {
|
||||
const value = localStorage.getItem(key);
|
||||
return value !== null ? value : defaultValue;
|
||||
}
|
||||
|
||||
function setSavedSetting(key, value) {
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
|
||||
function createSettingsIcon() {
|
||||
const button = document.createElement('button');
|
||||
button.type = 'button';
|
||||
button.className = 'paper-icon-button-light headerButton seasonal-settings-button';
|
||||
button.title = 'Seasonal Settings';
|
||||
button.innerHTML = '<span class="material-icons">ac_unit</span>';
|
||||
button.style.verticalAlign = 'middle';
|
||||
|
||||
button.addEventListener('click', (e) => {
|
||||
e.stopPropagation();
|
||||
toggleSettingsPopup(button);
|
||||
});
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
function createSettingsPopup(anchorElement) {
|
||||
const existing = document.querySelector('.seasonal-settings-popup');
|
||||
if (existing) existing.remove();
|
||||
|
||||
const popup = document.createElement('div');
|
||||
popup.className = 'seasonal-settings-popup dialog';
|
||||
|
||||
Object.assign(popup.style, {
|
||||
position: 'fixed',
|
||||
zIndex: '10000',
|
||||
backgroundColor: '#202020',
|
||||
padding: '1em',
|
||||
borderRadius: '4px',
|
||||
boxShadow: '0 0 20px rgba(0,0,0,0.5)',
|
||||
minWidth: '200px',
|
||||
color: '#fff'
|
||||
});
|
||||
|
||||
const rect = anchorElement.getBoundingClientRect();
|
||||
popup.style.top = `${rect.bottom + 10}px`;
|
||||
popup.style.right = `${window.innerWidth - rect.right}px`;
|
||||
|
||||
const enabledContainer = document.createElement('div');
|
||||
enabledContainer.style.marginBottom = '1em';
|
||||
enabledContainer.style.display = 'flex';
|
||||
enabledContainer.style.alignItems = 'center';
|
||||
enabledContainer.style.justifyContent = 'space-between';
|
||||
|
||||
const enabledLabel = document.createElement('label');
|
||||
enabledLabel.textContent = 'Enable Seasonals';
|
||||
enabledLabel.style.marginRight = '10px';
|
||||
|
||||
const enabledCheckbox = document.createElement('input');
|
||||
enabledCheckbox.type = 'checkbox';
|
||||
enabledCheckbox.checked = getSavedSetting('seasonals-enabled', 'true') === 'true';
|
||||
enabledCheckbox.addEventListener('change', (e) => {
|
||||
setSavedSetting('seasonals-enabled', e.target.checked);
|
||||
location.reload();
|
||||
});
|
||||
|
||||
enabledContainer.appendChild(enabledLabel);
|
||||
enabledContainer.appendChild(enabledCheckbox);
|
||||
popup.appendChild(enabledContainer);
|
||||
|
||||
// Theme Selector
|
||||
const themeContainer = document.createElement('div');
|
||||
themeContainer.style.display = 'flex';
|
||||
themeContainer.style.flexDirection = 'column';
|
||||
|
||||
const themeLabel = document.createElement('label');
|
||||
themeLabel.textContent = 'Force Theme';
|
||||
themeLabel.style.marginBottom = '0.5em';
|
||||
|
||||
const themeSelect = document.createElement('select');
|
||||
themeSelect.style.width = '100%';
|
||||
themeSelect.style.padding = '0.5em';
|
||||
themeSelect.style.backgroundColor = '#333';
|
||||
themeSelect.style.color = '#fff';
|
||||
themeSelect.style.border = '1px solid #444';
|
||||
themeSelect.style.borderRadius = '4px';
|
||||
|
||||
// Add Auto option
|
||||
const autoOption = document.createElement('option');
|
||||
autoOption.value = 'auto';
|
||||
autoOption.textContent = 'Auto (Date Based)';
|
||||
themeSelect.appendChild(autoOption);
|
||||
|
||||
// Add available themes
|
||||
Object.keys(themeConfigs).forEach(key => {
|
||||
if (key === 'none') return;
|
||||
const option = document.createElement('option');
|
||||
option.value = key;
|
||||
// Capitalize first letter
|
||||
option.textContent = key.charAt(0).toUpperCase() + key.slice(1);
|
||||
themeSelect.appendChild(option);
|
||||
});
|
||||
|
||||
// Set current value
|
||||
themeSelect.value = getSavedSetting('seasonals-theme', 'auto');
|
||||
|
||||
themeSelect.addEventListener('change', (e) => {
|
||||
setSavedSetting('seasonals-theme', e.target.value);
|
||||
location.reload();
|
||||
});
|
||||
|
||||
themeContainer.appendChild(themeLabel);
|
||||
themeContainer.appendChild(themeSelect);
|
||||
popup.appendChild(themeContainer);
|
||||
|
||||
// Close on click outside
|
||||
const closeHandler = (e) => {
|
||||
if (!popup.contains(e.target) && e.target !== anchorElement && !anchorElement.contains(e.target)) {
|
||||
popup.remove();
|
||||
document.removeEventListener('click', closeHandler);
|
||||
}
|
||||
};
|
||||
setTimeout(() => document.addEventListener('click', closeHandler), 0);
|
||||
|
||||
document.body.appendChild(popup);
|
||||
}
|
||||
|
||||
function toggleSettingsPopup(anchorElement) {
|
||||
const existing = document.querySelector('.seasonal-settings-popup');
|
||||
if (existing) {
|
||||
existing.remove();
|
||||
} else {
|
||||
createSettingsPopup(anchorElement);
|
||||
}
|
||||
}
|
||||
|
||||
function injectSettingsIcon() {
|
||||
const observer = new MutationObserver((mutations, obs) => {
|
||||
// Check if admin has enabled this feature
|
||||
if (window.SeasonalsPluginConfig && window.SeasonalsPluginConfig.EnableClientSideToggle === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
const headerRight = document.querySelector('.headerRight');
|
||||
if (headerRight && !document.querySelector('.seasonal-settings-button')) {
|
||||
const icon = createSettingsIcon();
|
||||
headerRight.prepend(icon);
|
||||
// obs.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
observer.observe(document.body, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
});
|
||||
}
|
||||
|
||||
injectSettingsIcon();
|
||||
|
||||
79
Jellyfin.Plugin.Seasonals/Web/test-site-new.html
Normal file
79
Jellyfin.Plugin.Seasonals/Web/test-site-new.html
Normal file
@@ -0,0 +1,79 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Seasonals Test Display</title>
|
||||
<link rel="stylesheet" href="snowfall.css">
|
||||
<style>
|
||||
body {
|
||||
background-color: black;
|
||||
color: white;
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
/* Mock Jellyfin Header */
|
||||
.skinHeader {
|
||||
background-color: #101010;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 1em;
|
||||
border-bottom: 1px solid #333;
|
||||
}
|
||||
|
||||
.headerRight {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.paper-icon-button-light {
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.material-icons {
|
||||
font-family: 'Material Icons';
|
||||
/* Placeholder if not loaded */
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-size: 24px;
|
||||
display: inline-block;
|
||||
line-height: 1;
|
||||
text-transform: none;
|
||||
letter-spacing: normal;
|
||||
word-wrap: normal;
|
||||
white-space: nowrap;
|
||||
direction: ltr;
|
||||
}
|
||||
</style>
|
||||
<!-- Load Material Icons for the snowflake -->
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!-- Mock Header Structure -->
|
||||
<div class="skinHeader">
|
||||
<div class="skinHeader-content">
|
||||
<span>Jellyfin Mock Header</span>
|
||||
</div>
|
||||
<div class="headerRight">
|
||||
<button class="paper-icon-button-light">
|
||||
<span class="material-icons">person</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="seasonals-container"></div>
|
||||
|
||||
<!-- Load the main script -->
|
||||
<script src="seasonals.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Reference in New Issue
Block a user