61 lines
3.5 KiB
JavaScript
61 lines
3.5 KiB
JavaScript
'use strict';
|
|
|
|
// Two navigation levels; scraper tabs stay within their section, not another menu.
|
|
const SETTINGS_SECTIONS = [
|
|
{ id: 'user/reading', group: 'User', title: 'Reading', hint: 'Reader modes, page spacing and zoom.' },
|
|
{ id: 'user/reading-time', group: 'User', title: 'Reading time & privacy', hint: 'Your clock, Reading Story and personal data.' },
|
|
{ id: 'user/notifications', group: 'User', title: 'Notifications', hint: 'Chapter alerts and your optional webhook.' },
|
|
{ id: 'user/account', group: 'User', title: 'Account', hint: 'Your account and password.' },
|
|
{ id: 'admin/scraper', group: 'Admin', title: 'Scraper', hint: 'Live activity, downloads, update checks and sources.' },
|
|
{ id: 'admin/library', group: 'Admin', title: 'Server & library', hint: 'Library location, scan status and maintenance.' },
|
|
{ id: 'admin/users', group: 'Admin', title: 'Users & access', hint: 'Manage accounts, passwords and administrators.' },
|
|
{ id: 'admin/collections', group: 'Admin', title: 'Collections & metadata', hint: 'Group manga and review their relationships.', href: 'collections.html' },
|
|
{ id: 'admin/health', group: 'Admin', title: 'Service health', hint: 'Service status, incidents and recovery.', href: 'health.html' },
|
|
];
|
|
const SCRAPER_TABS = ['activity', 'queue', 'updates', 'sources'];
|
|
|
|
function settingsRoute(hash, admin) {
|
|
const parts = String(hash || '').replace(/^#/, '').split('/');
|
|
const candidate = parts.slice(0, 2).join('/'), tab = parts[2];
|
|
const section = SETTINGS_SECTIONS.find(item => item.id === candidate && (admin || item.group === 'User'));
|
|
return { id: section?.id || 'user/reading', tab: SCRAPER_TABS.includes(tab) ? tab : tab === 'hosters' ? 'sources' : 'activity' };
|
|
}
|
|
|
|
function settingsShell(me, active = 'user/reading') {
|
|
const main = $('main'); main.textContent = '';
|
|
const nav = h('nav', { class: 'settings-menu', 'aria-label': 'Settings sections' });
|
|
const links = new Map();
|
|
for (const group of me.admin ? ['User', 'Admin'] : ['User']) {
|
|
nav.append(h('div', { class: 'settings-menu-group' }, h('h2', {}, group),
|
|
SETTINGS_SECTIONS.filter(section => section.group === group).map(section => {
|
|
const link = h('a', { href: section.href || 'settings.html#' + section.id }, section.title);
|
|
links.set(section.id, link);
|
|
return link;
|
|
})));
|
|
}
|
|
const crumb = h('p', { class: 'settings-breadcrumb muted small' });
|
|
const title = h('h1', { tabindex: '-1' });
|
|
const titleRow = h('div', { class: 'settings-title-row' }, title);
|
|
const hint = h('p', { class: 'muted settings-hint' });
|
|
const content = h('div', { class: 'settings-content' });
|
|
main.append(h('div', { class: 'settings-layout' }, nav,
|
|
h('div', { class: 'settings-workspace' }, crumb, titleRow, hint, content)));
|
|
function select(id, focus = false) {
|
|
const section = SETTINGS_SECTIONS.find(item => item.id === id);
|
|
if (!section || (section.group === 'Admin' && !me.admin)) return;
|
|
for (const [key, link] of links) {
|
|
if (key === id) link.setAttribute('aria-current', 'page');
|
|
else link.removeAttribute('aria-current');
|
|
}
|
|
crumb.textContent = 'Settings / ' + section.group;
|
|
title.textContent = section.title;
|
|
hint.textContent = section.hint;
|
|
document.title = section.title + ' · Settings · MangaCircus';
|
|
if (focus) title.focus({ preventScroll: true });
|
|
}
|
|
select(active);
|
|
return { content, titleRow, select };
|
|
}
|
|
|
|
if (typeof module !== 'undefined') module.exports = { SETTINGS_SECTIONS, SCRAPER_TABS, settingsRoute };
|