diora-web/static/js/app/20-tabs.js
mrwnslz 656520fe8e
Some checks failed
Build and push Docker image / build (push) Successful in 14s
Test / test (push) Successful in 8m23s
Test / e2e (push) Failing after 1m54s
Refactoring Schritt 2: app.js nach Bereichen in static/js/app/ aufgeteilt; sw v106
32 klassische Skripte in der alten Reihenfolge (01-core … 99-init), ohne
Änderung am Code darin: geschnitten nur zwischen Anweisungen, je Datei
'use strict' wie vorher für die ganze Datei. Geprüft (acorn): kein Code, der
beim Laden läuft, braucht einen Namen aus einer späteren Datei. Globale
Funktionen und let/const bleiben global, die App-Schichten (native.js,
native-eink.js, …) umhüllen sie wie bisher. Template und sw.js-SHELL listen
die Dateien; diora-android build_web nimmt static/js rekursiv.

Getestet: 14 Browser-Tests (e2e), Desktop-App (Buch, Lesestand, Leiste im
Reader, Hell/Dunkel).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 18:30:43 +02:00

67 lines
2.4 KiB
JavaScript

'use strict';
// ---------------------------------------------------------------------------
// Tabs
// ---------------------------------------------------------------------------
const TOP_TABS = ['home', 'radio', 'focus', 'podcasts', 'books', 'feeds', 'ambient'];
const RADIO_SUB_TABS = ['search', 'saved', 'history'];
// ---------------------------------------------------------------------------
// URL hash routing — makes tabs deep-linkable/shareable and lets the browser
// back/forward buttons move between them. Deliberately hash-based rather
// than real paths: /podcasts/ and /books/ etc. are already taken by the
// JSON API, so this avoids touching Django's urls.py at all.
// ---------------------------------------------------------------------------
let _routingFromHash = false;
let _currentTopTab = 'radio';
function _syncHash() {
if (_routingFromHash) return;
let hash = _currentTopTab;
if (_currentTopTab === 'radio') {
hash += '/' + (localStorage.getItem('diora_active_radio_tab') || 'saved');
}
if (location.hash.slice(1) !== hash) history.pushState(null, '', '#' + hash);
}
function _routeFromHash() {
const [tab, sub] = (location.hash || '').replace(/^#/, '').split('/').filter(Boolean);
if (!TOP_TABS.includes(tab)) return;
_routingFromHash = true;
try {
showTab(tab);
if (tab === 'radio' && RADIO_SUB_TABS.includes(sub)) showRadioTab(sub);
} finally {
_routingFromHash = false;
}
}
window.addEventListener('popstate', _routeFromHash);
window.addEventListener('hashchange', _routeFromHash);
function showOfflineBanner(offline) {
document.body.classList.toggle('app-offline', offline);
const uploadArea = $('book-upload-area');
if (uploadArea) uploadArea.style.display = offline ? 'none' : '';
}
function showTab(name) {
TOP_TABS.forEach(p => {
const panel = $(`tab-${p}`);
if (panel) panel.style.display = (p === name) ? '' : 'none';
});
document.querySelectorAll('#tabs .tab-btn').forEach((btn, i) => {
btn.classList.toggle('active', TOP_TABS[i] === name);
});
localStorage.setItem('diora_active_tab', name);
_currentTopTab = name;
_syncHash();
if (name === 'books') loadBookList();
if (name === 'ambient') _ambientLoad();
if (name === 'home' && window.__dioraHomeShow) window.__dioraHomeShow(); // static/js/home.js
if (name === 'feeds' && window.__dioraFeedsShow) window.__dioraFeedsShow(); // static/js/feeds.js
}