diff --git a/configuration/watchlist-configuration.js b/configuration/watchlist-configuration.js new file mode 100644 index 0000000..f496441 --- /dev/null +++ b/configuration/watchlist-configuration.js @@ -0,0 +1,185 @@ +// KefinTweaks Watchlist Configuration UI + +(function () { + 'use strict'; + + const LOG = (...args) => console.log('[KefinTweaks Watchlist Config]', ...args); + const ERR = (...args) => console.error('[KefinTweaks Watchlist Config]', ...args); + + const MODAL_ID = 'kefin-watchlist-config'; + + const DEFAULTS = { + sideMenu: true, + topNavigation: true, + userMenu: true + }; + + function normalizeConfig(raw) { + const cfg = raw && typeof raw === 'object' ? raw : {}; + return { + sideMenu: cfg.sideMenu !== false, + topNavigation: cfg.topNavigation !== false && cfg.topNavigation !== 'none', + userMenu: cfg.userMenu !== false + }; + } + + async function loadConfig() { + const config = window.KefinTweaksConfig || {}; + return normalizeConfig(config.watchlist); + } + + async function saveConfig(watchlistConfig) { + try { + if (!window.KefinTweaksConfig) { + window.KefinTweaksConfig = {}; + } + window.KefinTweaksConfig.watchlist = watchlistConfig; + + if (window.KefinTweaksUtils && window.KefinTweaksUtils.saveConfigToJavaScriptInjector) { + await window.KefinTweaksUtils.saveConfigToJavaScriptInjector(); + LOG('Configuration saved to JS Injector'); + return true; + } + if (window.KefinTweaksConfiguration && window.KefinTweaksConfiguration.findJavaScriptInjectorPlugin) { + const pluginId = await window.KefinTweaksConfiguration.findJavaScriptInjectorPlugin(); + if (pluginId) { + await window.KefinTweaksConfiguration.saveConfigToJavaScriptInjector(window.KefinTweaksConfig); + LOG('Configuration saved to JS Injector (fallback)'); + return true; + } + } + ERR('saveConfigToJavaScriptInjector not available'); + return false; + } catch (error) { + ERR('Error saving config:', error); + return false; + } + } + + function buildToggleCard(id, checked, label, description) { + if (typeof window.KefinTweaksUI?.buildToggleCard === 'function') { + return window.KefinTweaksUI.buildToggleCard(id, checked, label, description); + } + return ` +
+ + ${description ? `
${description}
` : ''} +
+ `; + } + + function buildConfigHTML(config) { + return ` +
+ ${buildToggleCard( + 'wl-side-menu', + config.sideMenu, + 'Side menu', + 'Show a Watchlist link in the left navigation drawer.' + )} + ${buildToggleCard( + 'wl-top-navigation', + config.topNavigation, + 'Top navigation', + 'Show Watchlist in the main top navigation bar (or via Custom Tabs when available).' + )} + ${buildToggleCard( + 'wl-user-menu', + config.userMenu, + 'User menu', + 'Show a Watchlist link in the user settings menu.' + )} +
+ `; + } + + function collectConfig() { + const side = document.getElementById('wl-side-menu'); + const top = document.getElementById('wl-top-navigation'); + const user = document.getElementById('wl-user-menu'); + return { + sideMenu: side ? side.checked : DEFAULTS.sideMenu, + topNavigation: top ? top.checked : DEFAULTS.topNavigation, + userMenu: user ? user.checked : DEFAULTS.userMenu + }; + } + + function attachToggleHandlers(root) { + if (typeof window.KefinTweaksUI?.bindToggleCards === 'function') { + window.KefinTweaksUI.bindToggleCards(root); + return; + } + root.querySelectorAll('.kefin-toggle-switch').forEach((btn) => { + if (btn.dataset.kefinToggleBound === 'true') return; + btn.dataset.kefinToggleBound = 'true'; + btn.addEventListener('click', () => { + const next = btn.dataset.enabled !== 'true'; + btn.dataset.enabled = String(next); + btn.setAttribute('aria-pressed', String(next)); + const checkboxId = btn.dataset.checkboxId; + const checkbox = checkboxId ? document.getElementById(checkboxId) : null; + if (checkbox) checkbox.checked = next; + }); + }); + } + + async function openConfigModal() { + try { + const config = await loadConfig(); + const content = document.createElement('div'); + content.innerHTML = buildConfigHTML(config); + + const footer = document.createElement('div'); + footer.style.display = 'flex'; + footer.style.gap = '0.75em'; + footer.style.justifyContent = 'flex-end'; + footer.innerHTML = ` + + + `; + + const modalInstance = window.ModalSystem.create({ + id: MODAL_ID, + title: 'Watchlist', + content, + footer, + closeOnBackdrop: true, + closeOnEscape: true, + showCloseButton: true, + onOpen: (modal) => { + attachToggleHandlers(modal.dialogContent || content); + const saveBtn = modal.dialogFooter.querySelector('#save-watchlist-config-btn'); + if (saveBtn) { + saveBtn.addEventListener('click', async () => { + const next = collectConfig(); + const success = await saveConfig(next); + if (success) { + window.KefinTweaksToaster?.toast?.('Watchlist configuration saved!'); + window.ModalSystem.close(MODAL_ID); + } else { + alert('Error saving configuration. Please ensure the JavaScript Injector plugin is installed and you have administrator permissions.'); + } + }); + } + } + }); + + if (modalInstance?.dialog) { + modalInstance.dialog.style.maxWidth = '90vw'; + modalInstance.dialog.style.width = '560px'; + } + LOG('Watchlist configuration modal opened'); + } catch (error) { + ERR('Error opening Watchlist config modal:', error); + alert('Error opening configuration: ' + error.message); + } + } + + window.KefinTweaksFeatureConfigs = window.KefinTweaksFeatureConfigs || {}; + window.KefinTweaksFeatureConfigs.watchlist = { openConfigModal, DEFAULTS, normalizeConfig }; + + console.log('[KefinTweaks Watchlist Config] Script loaded'); +})(); diff --git a/scripts/configuration.js b/scripts/configuration.js index 2ec530b..1ccfeb3 100644 --- a/scripts/configuration.js +++ b/scripts/configuration.js @@ -1177,7 +1177,7 @@ // Build script toggle switches function buildScriptToggles(scripts) { // Features that have configuration options (not just toggles) - const featuresWithConfig = new Set(['homeScreen', 'search', 'flattenSingleSeasonShows', 'skinManager', 'customMenuLinks', 'seriesInfo', 'versionPreferences', 'thumbnailScrubber', 'watchTogether', 'userManager']); + const featuresWithConfig = new Set(['homeScreen', 'search', 'flattenSingleSeasonShows', 'skinManager', 'customMenuLinks', 'seriesInfo', 'versionPreferences', 'thumbnailScrubber', 'watchTogether', 'watchlist', 'userManager']); const scriptNames = [ { key: 'watchlist', label: 'Watchlist', desc: 'Allows your users to add items to their Watchlist. The Watchlist page shows an overview of all items on a user\'s Watchlist, as well as their Series Progress and Movie History. It also includes a Statistics page with an overview of your user watched stats.' }, diff --git a/scripts/watchlist.js b/scripts/watchlist.js index 0af9596..31110fa 100644 --- a/scripts/watchlist.js +++ b/scripts/watchlist.js @@ -7092,7 +7092,7 @@ In the Custom Tabs plugin, add a new tab with the following HTML content: }); function checkForEmptyWatchlist() { - const onWatchlistPage = window.location.pathname.includes(window.KefinTweaksUtils._watchlistUrl || '#/watchlist'); + const onWatchlistPage = window.location.pathname.includes(window.KefinTweaksUtils._watchlistUrl) || window.location.hash.includes(window.KefinTweaksUtils._watchlistUrl); if (!onWatchlistPage) { return; @@ -7398,9 +7398,8 @@ In the Custom Tabs plugin, add a new tab with the following HTML content: // Optional onViewPage backup (primary mount path is MutationObserver + addCustomPage) if (window.KefinTweaksUtils && window.KefinTweaksUtils.onViewPage) { window.KefinTweaksUtils.onViewPage(async (view, element, hash) => { - const path = String(hash || '').split('?')[0]; const watchlistPath = window.KefinTweaksUtils._watchlistUrl || '#/watchlist'; - if (path.includes(watchlistPath)) { + if (hash.includes(watchlistPath)) { LOG('onViewPage: Watchlist'); renderWatchlist(); } @@ -7412,13 +7411,14 @@ In the Custom Tabs plugin, add a new tab with the following HTML content: WARN('KefinTweaksUtils.onViewPage not available'); } - function addCustomWatchlistPage() { + async function addCustomWatchlistPage() { if (!window.KefinTweaksUtils?.addCustomPage) { WARN('addCustomPage not available yet'); return false; } + const watchlistUrl = await window.KefinTweaksUtils.getWatchlistUrl(); window.KefinTweaksUtils.addCustomPage( - '#/watchlist', + watchlistUrl, 'Watchlist', '
' ); @@ -7433,21 +7433,32 @@ In the Custom Tabs plugin, add a new tab with the following HTML content: return; } + const watchlistCfg = window.KefinTweaksFeatureConfigs?.watchlist?.normalizeConfig?.( + window.KefinTweaksConfig?.watchlist + ) || { + sideMenu: window.KefinTweaksConfig?.watchlist?.sideMenu !== false, + topNavigation: window.KefinTweaksConfig?.watchlist?.topNavigation !== false + && window.KefinTweaksConfig?.watchlist?.topNavigation !== 'none', + userMenu: window.KefinTweaksConfig?.watchlist?.userMenu !== false + }; + const watchlistTabIndex = await getWatchlistTabIndex(); const options = { - userMenu: true, + sideMenu: watchlistCfg.sideMenu !== false, + userMenu: watchlistCfg.userMenu === true, order: 2 }; let watchlistUrl = '#/watchlist'; - // Major version of Jellyfin - const jellyfinVersion = await window.KefinTweaks.getJellyfinMajorVersion(); - const isModernUI = document.querySelector('.MuiBox-root') !== null || localStorage.getItem('layout')?.length === 0; + const isModernUI = window.KefinTweaksUtils.isModernUI(); + const wantTopNav = watchlistCfg.topNavigation !== false && watchlistCfg.topNavigation !== 'none'; // If watchlist tab index is null or undefined, add to top navigation - if (watchlistTabIndex === null || watchlistTabIndex === undefined || isModernUI) { + if (wantTopNav && (watchlistTabIndex === null || watchlistTabIndex === undefined || isModernUI)) { options.topNavigation = 'main'; + } else if (!wantTopNav) { + options.topNavigation = 'none'; } else { watchlistUrl = `#/home?tab=${watchlistTabIndex}`; }