Books: offline UX — Banner, Auto-Tab, klare Fehlermeldung
- Bei Offline-Start oder Wechsel in Flugzeugmodus: roter Banner unterhalb der Navbar, automatischer Wechsel zum Books-Tab (einziger sinnvoller Tab offline), Upload-Zone wird ausgeblendet - Bei Rückkehr online: Banner verschwindet, Upload-Zone erscheint wieder, Buchliste wird neu synchronisiert - openBook unterscheidet jetzt zwischen "nicht gecacht" (Toast-Hinweis zum einmaligen Online-Öffnen) und echtem Defekt (markBookBroken wie bisher) — kein stilles Scheitern mehr Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
a96591c63f
commit
aa562f8b4d
2 changed files with 61 additions and 3 deletions
|
|
@ -1486,6 +1486,20 @@ body.dnd-mode .timer-display {
|
|||
padding: 4px 0 8px;
|
||||
}
|
||||
|
||||
.offline-banner {
|
||||
position: fixed;
|
||||
top: var(--nav-h);
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: var(--accent);
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
padding: 4px 8px;
|
||||
z-index: 250;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.book-key-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
|
|
|||
|
|
@ -1039,6 +1039,25 @@ function editNotes(pk, current) {
|
|||
const TOP_TABS = ['radio', 'focus', 'podcasts', 'books'];
|
||||
const RADIO_SUB_TABS = ['search', 'saved', 'history'];
|
||||
|
||||
function showOfflineBanner(offline) {
|
||||
let banner = $('offline-banner');
|
||||
if (offline) {
|
||||
if (!banner) {
|
||||
banner = document.createElement('div');
|
||||
banner.id = 'offline-banner';
|
||||
banner.className = 'offline-banner';
|
||||
banner.textContent = 'Offline — nur gecachte Bücher verfügbar';
|
||||
document.body.prepend(banner);
|
||||
}
|
||||
const uploadArea = $('book-upload-area');
|
||||
if (uploadArea) uploadArea.style.display = 'none';
|
||||
} else {
|
||||
if (banner) banner.remove();
|
||||
const uploadArea = $('book-upload-area');
|
||||
if (uploadArea) uploadArea.style.display = '';
|
||||
}
|
||||
}
|
||||
|
||||
function showTab(name) {
|
||||
TOP_TABS.forEach(p => {
|
||||
const panel = $(`tab-${p}`);
|
||||
|
|
@ -3380,7 +3399,19 @@ async function openBook(bookId) {
|
|||
if (cached) {
|
||||
({data_ct, data_iv} = cached);
|
||||
} else {
|
||||
const res = await fetch(`/books/${bookId}/data/`);
|
||||
let res;
|
||||
try {
|
||||
res = await fetch(`/books/${bookId}/data/`);
|
||||
} catch {
|
||||
// offline and book not cached yet
|
||||
overlay.style.display = 'none';
|
||||
const toast = document.createElement('div');
|
||||
toast.className = 'reader-toast';
|
||||
toast.textContent = 'Buch nicht offline verfügbar — einmal online öffnen zum Cachen.';
|
||||
document.body.appendChild(toast);
|
||||
setTimeout(() => toast.remove(), 3500);
|
||||
return;
|
||||
}
|
||||
({data_ct, data_iv} = await res.json());
|
||||
_setCachedBook(bookId, data_ct, data_iv); // fire-and-forget
|
||||
}
|
||||
|
|
@ -5053,12 +5084,25 @@ function openRadioSidebar() {
|
|||
// Init book drop zone
|
||||
initBookDropZone();
|
||||
|
||||
// Restore last active tab
|
||||
// Restore last active tab — when offline, always go to books (most useful)
|
||||
const savedTab = localStorage.getItem('diora_active_tab') || 'radio';
|
||||
const savedRadioTab = localStorage.getItem('diora_active_radio_tab') || 'saved';
|
||||
showTab(savedTab);
|
||||
showTab(!navigator.onLine && IS_AUTHENTICATED ? 'books' : savedTab);
|
||||
showRadioTab(savedRadioTab);
|
||||
|
||||
// React to connectivity changes
|
||||
window.addEventListener('offline', () => {
|
||||
showOfflineBanner(true);
|
||||
if (IS_AUTHENTICATED) showTab('books');
|
||||
});
|
||||
window.addEventListener('online', () => {
|
||||
showOfflineBanner(false);
|
||||
loadBookList(); // sync any queued changes
|
||||
});
|
||||
|
||||
// Show banner immediately if already offline at startup
|
||||
if (!navigator.onLine) showOfflineBanner(true);
|
||||
|
||||
// Hourly background feed refresh (only when authenticated)
|
||||
if (IS_AUTHENTICATED) {
|
||||
setInterval(refreshAllFeeds, 60 * 60 * 1000);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue