Story Bell Logo Story Bell

Story Bell

Discover children’s stories with read-aloud audio, word highlighting, and illustrations. Safe, age-grouped tales with discussion questions for 0–12 year olds.

Stories that speak, words that glow.
if (!window.firebase) { console.error("Firebase SDK not found"); } let _sbUI; function ensureUI(){ if (!_sbUI) _sbUI = new firebaseui.auth.AuthUI(firebase.auth()); return _sbUI; } window.openAuthModal = function(){ var modal = document.getElementById('auth-modal'); if(!modal){ console.warn('auth-modal not found'); return; } // Show modal immediately modal.style.display = 'flex'; // Ensure container exists inside modal var container = document.getElementById('firebaseui-auth-container'); if(!container){ var content = modal.querySelector('.auth-modal-content, .modal-content, [role="dialog"]') || modal; container = document.createElement('div'); container.id = 'firebaseui-auth-container'; content.appendChild(container); } // Make sure container is visible container.style.display = 'block'; container.style.minHeight = '140px'; // Helper: start FirebaseUI with singleton and safe defaults function startUI(){ try{ if(!(window.firebase && window.firebaseui && firebase.auth)) return false; var ui = window.firebaseui.auth.AuthUI.getInstance() || new firebaseui.auth.AuthUI(firebase.auth()); var cfg = { signInFlow: 'popup', credentialHelper: (window.firebaseui && firebaseui.auth && firebaseui.auth.CredentialHelper) ? firebaseui.auth.CredentialHelper.NONE : undefined, signInOptions: [ firebase.auth.GoogleAuthProvider.PROVIDER_ID, firebase.auth.EmailAuthProvider.PROVIDER_ID ], callbacks: { signInSuccessWithAuthResult: function(){ if (typeof window.closeAuthModal==='function') window.closeAuthModal(); return false; } } }; ui.start('#firebaseui-auth-container', cfg); return true; }catch(e){ console.error('startUI error', e); return false; } } // If project defines a hook, let it run first (it may build custom menu/UI) try { if (typeof window.onOpenAuthModalHook === 'function') window.onOpenAuthModalHook(); } catch(e){ console.error(e); } // Robust first-open: retry until buttons appear (max ~1s) var tries = 0; function needButtons(){ var has = container.querySelector('.firebaseui-idp-button, .firebaseui-idp-list, .firebaseui-card-content, .firebaseui-provider-wrapper'); return !has; } function tick(){ // Start if not already started if (needButtons()) { startUI(); } tries++; if (needButtons() && tries < 7) { // Wait a frame + small delay to allow CSS/layout & hook work setTimeout(tick, 150); } } // Kick off after a frame so modal is in the layout if (document.startViewTransition) { // No-op; just schedule requestAnimationFrame(tick); } else { requestAnimationFrame(tick); } }; window.closeAuthModal = function(){ const m = document.getElementById('auth-modal'); if (m) m.style.display = 'none'; }; window.signOutFirebase = function(){ if (firebase && firebase.auth) { firebase.auth().signOut().then(function(){ alert('Signed out'); }).catch(console.error); } }; // Child profile storage (for Google sign-in users) function getChildProfile(uid) { try { return JSON.parse(localStorage.getItem('storybell_child_' + uid) || 'null'); } catch(e) { return null; } } // Get localStorage user (for email sign-up users) function getLocalUser() { try { return JSON.parse(localStorage.getItem('storybell_user') || 'null'); } catch(e) { return null; } } // Capitalize first letter of a string function capitalizeFirst(str) { if (!str) return str; return str.charAt(0).toUpperCase() + str.slice(1); } // Update header UI based on auth state (supports both Firebase and localStorage users) function updateHeaderAuth(firebaseUser) { // Desktop elements var loginBtnDesktop = document.querySelector('.login-cta-header'); var profileDesktop = document.querySelector('.sb-user-profile'); // Mobile hamburger elements var loginMobile = document.querySelector('.login-mobile'); var profileMobile = document.querySelector('.login-mobile-profile'); var nameMobile = document.querySelector('.sb-user-name-mobile'); // Subscribe box (hide when logged in) var subscribeBox = document.querySelector('.subscribe-box'); // Check for email-based user in localStorage var localUser = getLocalUser(); // Either Firebase user OR localStorage user counts as "signed in" var isSignedIn = firebaseUser || localUser; if (isSignedIn) { var displayName; if (firebaseUser) { // Firebase user (Google sign-in): check child profile first var profile = getChildProfile(firebaseUser.uid); displayName = (profile && profile.name) || firebaseUser.displayName || (firebaseUser.email ? firebaseUser.email.split('@')[0] : 'Reader'); } else { // localStorage user (email sign-up): use stored name or email prefix displayName = localUser.name || (localUser.email ? localUser.email.split('@')[0] : 'Reader'); } // Capitalize first letter displayName = capitalizeFirst(displayName); // Desktop: hide login, show profile with name if (loginBtnDesktop) loginBtnDesktop.style.display = 'none'; if (profileDesktop) { profileDesktop.style.display = 'flex'; var nameEl = profileDesktop.querySelector('.sb-user-name'); if (nameEl) nameEl.textContent = displayName; } // Mobile hamburger: hide login, show profile with name if (loginMobile) loginMobile.classList.add('sb-auth-hidden'); if (profileMobile) profileMobile.style.display = 'block'; if (nameMobile) nameMobile.textContent = displayName; // Hide subscribe box for logged-in users if (subscribeBox) subscribeBox.style.display = 'none'; console.log('[SB] Header updated: showing', displayName); } else { // Desktop: show login, hide profile if (loginBtnDesktop) loginBtnDesktop.style.display = ''; if (profileDesktop) profileDesktop.style.display = 'none'; // Mobile hamburger: show login, hide profile if (loginMobile) loginMobile.classList.remove('sb-auth-hidden'); if (profileMobile) profileMobile.style.display = 'none'; // Show subscribe box for logged-out users if (subscribeBox) subscribeBox.style.display = 'block'; console.log('[SB] Header updated: showing login button'); } } // Sign out function (clears both Firebase and localStorage) window.signOutStoryBell = function(){ // Clear localStorage user (email sign-up) localStorage.removeItem('storybell_user'); localStorage.removeItem('storybell_simple_user'); // Also sign out Firebase (Google users) if (window.firebase && firebase.auth) { firebase.auth().signOut().then(function(){ window.location.href = '/'; }).catch(function(e){ console.error('[SB] Sign out error:', e); window.location.href = '/'; }); } else { window.location.href = '/'; } }; // Auth state listener to update header (for Firebase/Google users) if (window.firebase && firebase.auth) { console.log("[SB] Firebase initialized, checking auth..."); // Handle redirect result first (for Google sign-in redirect flow) firebase.auth().getRedirectResult().then(function(result) { console.log("[SB] getRedirectResult:", result); if (result && result.user) { console.log("[SB] Redirect sign-in successful:", result.user.displayName || result.user.email); updateHeaderAuth(result.user); } else { console.log("[SB] No redirect result (user may not have just signed in via redirect)"); } }).catch(function(error) { console.error("[SB] Redirect result error:", error.code, error.message); }); // Check current user immediately var currentUser = firebase.auth().currentUser; console.log("[SB] Current user on load:", currentUser ? currentUser.email : "none"); // Also listen for auth state changes firebase.auth().onAuthStateChanged(function(u){ console.log("[SB] Firebase auth state:", u ? "SIGNED IN as " + (u.displayName || u.email) : "SIGNED OUT"); updateHeaderAuth(u); }); } // Also check for localStorage user on page load (for email sign-up users) // Run after DOM is ready to ensure header elements exist function initLocalUserAuth() { console.log('[SB] initLocalUserAuth running...'); var localUser = getLocalUser(); console.log('[SB] localStorage user:', localUser); var loginBtns = document.querySelectorAll('.login-cta-header, .login-mobile a[href*="signin"]'); var profileEls = document.querySelectorAll('.sb-user-profile'); console.log('[SB] Found loginBtns:', loginBtns.length, 'profileEls:', profileEls.length); if (localUser) { console.log('[SB] Calling updateHeaderAuth for:', localUser.name || localUser.email); updateHeaderAuth(null); // Pass null for firebase, will use localStorage } } // Run when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initLocalUserAuth); } else { initLocalUserAuth(); } document.addEventListener('click', function(e){ const m = document.getElementById('auth-modal'); if (!m) return; if (e.target === m) closeAuthModal(); }); // Start/refresh FirebaseUI with Google + Email when auth modal opens window.onOpenAuthModalHook = function(){ try { var containerSel = '#firebaseui-auth-container'; // Ensure container exists var container = document.querySelector(containerSel); if (!container) { var m = document.getElementById('auth-modal'); if (!m) return; container = document.createElement('div'); container.id = 'firebaseui-auth-container'; m.querySelector('.auth-inner, .auth-modal__inner, .auth-content')?.appendChild(container) || m.appendChild(container); } // Initialize UI var ui; if (typeof ensureUI === 'function') { ui = ensureUI(); } else if (window.firebaseui && firebase && firebase.auth) { ui = new firebaseui.auth.AuthUI(firebase.auth()); } else { console.warn('FirebaseUI not available yet.'); return; } // Start UI ui.start(containerSel, { signInFlow: 'popup', credentialHelper: firebaseui.auth.CredentialHelper.NONE, signInOptions: [ firebase.auth.GoogleAuthProvider.PROVIDER_ID, firebase.auth.EmailAuthProvider.PROVIDER_ID ], callbacks: { signInSuccessWithAuthResult: function(){ if (typeof window.closeAuthModal === 'function') window.closeAuthModal(); return false; } } }); } catch(e){ console.error('onOpenAuthModalHook error:', e); } };
Make Reading List