// Bucketlist tab — password-gated, real-time Firestore list
(() => {
const FIREBASE_CONFIG = {
apiKey: "AIzaSyDuSjEGEKx5FnWYnQq8f_owbpYRBRrl5x0",
authDomain: "esef-514bf.firebaseapp.com",
projectId: "esef-514bf",
storageBucket: "esef-514bf.firebasestorage.app",
messagingSenderId: "353969651290",
appId: "1:353969651290:web:41e3aaf8e54c743702a263"
};
// ═══════════════════════════════════════════════════════════
// PASSWORD GATE — items never touch DOM until authenticated
// ═══════════════════════════════════════════════════════════
// --- SHA-256 via Web Crypto ---------------------------------------------------
async function sha256(text) {
const buf = new TextEncoder().encode(text);
const hash = await crypto.subtle.digest("SHA-256", buf);
return Array.from(new Uint8Array(hash))
.map(b => b.toString(16).padStart(2, "0"))
.join("");
}
// --- Lock-screen HTML (only thing in DOM before auth) -------------------------
function showLock(container) {
container.innerHTML = `
`;
}
// --- Build the real panel + attach Firestore after auth -----------------------
function unlock(container, db, hash) {
container.innerHTML = `
`;
const listEl = document.getElementById("bl-list");
if (!listEl) return;
// items live under bucketlist_data//items — the path IS the key
db.collection("bucketlist_data").doc(hash).collection("items").onSnapshot(snap => {
const items = [];
snap.forEach(doc => {
const d = doc.data();
items.push({
id: doc.id,
title: d.title || "",
description: d.description || "",
date: d.date || "",
utc: d.utc || "",
done: d.done || false
});
});
items.sort((a, b) => (a.utc || "").localeCompare(b.utc || ""));
render(listEl, items);
}, () => {
if (listEl) listEl.innerHTML = 'Could not load.
';
});
}
function render(listEl, items) {
if (items.length === 0) {
listEl.innerHTML = 'Nothing here yet.
';
return;
}
listEl.innerHTML = items.map(item =>
'' +
'
' + esc(item.title) + '
' +
(item.date ? '
' + esc(item.date) + '
' : '') +
(item.description ? '
' + esc(item.description) + '
' : '') +
'
'
).join("");
}
function esc(s) {
if (!s) return "";
return String(s).replace(/&/g,"&").replace(//g,">").replace(/"/g,""").replace(/'/g,"'");
}
// ═══════════════════════════════════════════════════════════
// ENTRY
// ═══════════════════════════════════════════════════════════
document.addEventListener("DOMContentLoaded", () => {
const tab = document.getElementById("bucketlist");
if (!tab) return;
if (!firebase.apps.length) firebase.initializeApp(FIREBASE_CONFIG);
const db = firebase.firestore();
const container = tab; // the itself — nothing else exists inside it
// --- Already authenticated this session? ---------------------------------
const savedHash = sessionStorage.getItem("bl_hash");
if (savedHash) {
unlock(container, db, savedHash);
return;
}
// --- Show lock screen ----------------------------------------------------
showLock(container);
const userInput = document.getElementById("bl-user");
const passInput = document.getElementById("bl-pass");
const submitBtn = document.getElementById("bl-submit");
const errEl = document.getElementById("bl-err");
async function tryUnlock() {
const user = userInput.value.trim();
const pass = passInput.value;
if (!user || !pass) {
(user ? passInput : userInput).focus();
return;
}
submitBtn.disabled = true;
errEl.textContent = "";
// hash = sha256("username:password") — both must match the Firestore doc ID
const hash = await sha256(user + ":" + pass);
try {
const doc = await db.collection("bucketlist_auth").doc(hash).get();
if (doc.exists) {
sessionStorage.setItem("bl_hash", hash);
unlock(container, db, hash);
} else {
errEl.textContent = "Wrong credentials.";
passInput.value = "";
passInput.focus();
}
} catch (e) {
errEl.textContent = "Could not verify. Try again.";
}
submitBtn.disabled = false;
}
// Enter in either field submits the form
document.getElementById("bl-form").onsubmit = e => {
e.preventDefault();
tryUnlock();
};
});
})();