Files
MangaCircus/public/settings-nav.js

73 lines
4.4 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
'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-hours', group: 'User', title: 'Reading hours', hint: 'When this server allows chapter reading.' },
{ 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/peering', group: 'Admin', title: 'Peering', hint: 'Pair servers and manage their permissions.', href: 'peers.html#admin' },
{ id: 'admin/peer-catalog', group: 'Admin', title: 'Peer catalogs', hint: 'Approve titles to share and browse peer libraries.', href: 'peer-catalog.html' },
{ 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'];
const READING_DAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
function readingDayText(schedule, index) {
const previous = (index + 6) % 7;
const carried = schedule.days[previous].filter(([start, end]) => end < start)
.map(([, end]) => `00:00–${end} (from ${READING_DAYS[previous]})`);
const starting = schedule.days[index].map(([start, end]) => `${start}–${end}${end < start ? ' (next day)' : ''}`);
return [...carried, ...starting].join(' · ') || 'Closed all day';
}
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, readingDayText };