diora-web/static/js/app/99-init.js

154 lines
5.6 KiB
JavaScript
Raw Normal View History

'use strict';
// ---------------------------------------------------------------------------
// Init
// ---------------------------------------------------------------------------
(function init() {
// Migrate PBKDF2-derived key stored by login/register form
if (window.USER_ID) {
const pending = localStorage.getItem('diora_pending_enc_key');
if (pending) {
localStorage.setItem(`diora_enc_key_${window.USER_ID}`, pending);
localStorage.removeItem('diora_pending_enc_key');
}
}
// Populate saved stations from server-side context if available
if (typeof INITIAL_SAVED !== 'undefined' && Array.isArray(INITIAL_SAVED)) {
// The server already renders saved stations in the template; nothing extra needed.
// But if JS-rendered saved tab were needed we'd call addSavedRow here.
}
// Restore persisted volume, fall back to slider default
const volSlider = $('volume');
if (volSlider) {
const saved = localStorage.getItem('diora_volume');
const vol = saved !== null ? parseInt(saved, 10) : parseInt(volSlider.value, 10);
setVolume(vol);
}
// Restore persisted read-aloud language
const ttsLangSelect = $('reader-tts-lang');
if (ttsLangSelect) ttsLangSelect.value = ttsLang;
// Load recommendations on page load
loadRecommendations();
// Initialise focus timer display
renderTimer();
// Initialise mood/genre chips
initMoodChips();
// Initialise curated station lists
initCuratedLists();
// Show curated lists again when search input is cleared
const searchInput = document.getElementById('search-input');
if (searchInput) {
searchInput.addEventListener('input', function () {
if (this.value === '') {
const curated = document.getElementById('curated-lists');
if (curated) curated.style.display = '';
}
});
}
// Load focus session stats
loadFocusStats();
// Apply encrypted wallpaper (if set)
applyEncryptedBackground();
// Init book drop zone
initBookDropZone();
const showReadToggle = $('book-show-read-toggle');
if (showReadToggle) showReadToggle.checked = _bookShowRead;
// Restore active tab: a URL hash (shared/bookmarked link, or browser
// back/forward) wins over the last-used tab remembered in localStorage —
// except when offline, where books (usable offline) always takes over.
const offlineOverride = !navigator.onLine && IS_AUTHENTICATED;
const hashTab = (location.hash || '').replace(/^#/, '').split('/')[0];
if (window.DioraNav) { // sections (static/js/nav.js)
DioraNav.restore(offlineOverride);
showRadioTab(localStorage.getItem('diora_active_radio_tab') || 'saved');
} else if (!offlineOverride && TOP_TABS.includes(hashTab)) {
_routeFromHash();
} else {
const savedTab = localStorage.getItem('diora_active_tab') || 'radio';
const savedRadioTab = localStorage.getItem('diora_active_radio_tab') || 'saved';
showTab(offlineOverride ? 'books' : savedTab);
showRadioTab(savedRadioTab);
}
// React to connectivity changes
window.addEventListener('offline', () => {
showOfflineBanner(true);
if (IS_AUTHENTICATED) showTab('books');
});
window.addEventListener('online', async () => {
showOfflineBanner(false);
await _syncPendingAnnotations();
loadBookList(); // sends the positions read offline first (the progress outbox)
});
// Show banner immediately if already offline at startup
if (!navigator.onLine) showOfflineBanner(true);
// Reading position: positions reached by the reader's own doing count (see _noteReadingPosition).
{
const content = $('reader-content');
if (content) {
for (const type of ['wheel', 'touchstart', 'mousedown']) {
content.addEventListener(type, _noteReaderInteraction, {passive: true, capture: true});
}
}
document.addEventListener('keydown', () => { if (currentBookId) _noteReaderInteraction(); }, true);
if (IS_AUTHENTICATED) _flushProgressOutbox();
}
// Footnote hover (desktop) + tap (mobile) via event delegation
const _readerContentEl = $('reader-content');
if (_readerContentEl) {
_readerContentEl.addEventListener('mouseover', e => {
const link = e.target.closest('[data-footnote-ref]');
if (!link) return;
clearTimeout(_footnotePopoverTimer);
_footnotePopoverTimer = setTimeout(() => showFootnotePopover(link), 120);
});
_readerContentEl.addEventListener('mouseout', e => {
if (!e.target.closest('[data-footnote-ref]')) return;
_footnotePopoverTimer = setTimeout(dismissFootnotePopover, 250);
});
_readerContentEl.addEventListener('click', e => {
const link = e.target.closest('[data-footnote-ref]');
if (!link) return;
e.preventDefault();
if (currentFootnotePopover?._refLink === link) {
dismissFootnotePopover();
} else {
showFootnotePopover(link);
}
});
// Text selection → highlight color/note popover
_readerContentEl.addEventListener('mouseup', handleReaderSelection);
_readerContentEl.addEventListener('touchend', e => {
const target = e.target;
const touch = e.changedTouches && e.changedTouches[0];
const clientX = touch ? touch.clientX : undefined;
const clientY = touch ? touch.clientY : undefined;
setTimeout(() => handleReaderSelection({target, clientX, clientY}), 50);
});
}
// Margin ("page margin") panel — click a marker to view/edit, click empty
// space to anchor a new freeform note at that line's height
const _readerMarginEl = $('reader-margin');
if (_readerMarginEl) {
_readerMarginEl.addEventListener('click', handleMarginClick);
}
})();