import { html } from "../util.js";
import AuthSession from "../sessionAuth.js";
import buttonCSS from "../buttonStyles.js";
import { escapeHtml } from "../escapeHtml.js";
import {
fallbackToRedirect,
openAuthPopup,
waitForAuthPopup,
} from "../authPopup.js";
class PxmeHeader extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: "open" });
}
static get observedAttributes() {
return ["active"];
}
attributeChangedCallback() {
if (this._rendered) this._updateActive();
}
connectedCallback() {
this._render();
this._initThemeToggle();
this._initAuth();
}
_render() {
const active = this.getAttribute("active") || "gallery";
this.shadowRoot.innerHTML = html`
`;
this._rendered = true;
this._updateActive();
}
_initThemeToggle() {
const toggle = this.shadowRoot.getElementById("theme-toggle");
const sunIcon = this.shadowRoot.getElementById("theme-icon-sun");
const moonIcon = this.shadowRoot.getElementById("theme-icon-moon");
const logo = this.shadowRoot.getElementById("header-logo");
const updateThemeUI = () => {
const isDark =
document.documentElement.getAttribute("data-theme") === "dark";
sunIcon.classList.toggle("pxme-theme-toggle__icon--hidden", !isDark);
moonIcon.classList.toggle("pxme-theme-toggle__icon--hidden", isDark);
logo.src = isDark ? "./img/pixme-dark.svg" : "./img/pixme.svg";
toggle.setAttribute(
"aria-label",
isDark ? "Switch to light mode" : "Switch to dark mode"
);
};
updateThemeUI();
toggle.addEventListener("click", () => {
const current = document.documentElement.getAttribute("data-theme");
const next = current === "dark" ? "light" : "dark";
document.documentElement.setAttribute("data-theme", next);
localStorage.setItem("pxme-theme", next);
updateThemeUI();
});
}
_updateActive() {
const active = this.getAttribute("active") || "gallery";
const links = this.shadowRoot.querySelectorAll(".pxme-nav__link[data-page]");
links.forEach((link) => {
link.classList.toggle(
"pxme-nav__link--active",
link.dataset.page === active
);
});
}
async _initAuth() {
const { isLoggedIn, getSession, isAdmin } = AuthSession({
authUri: "/auth/session",
});
if (await isLoggedIn()) {
const session = await getSession();
const loginItem = this.shadowRoot.getElementById("login-item");
const navLinks = this.shadowRoot.getElementById("nav-links");
loginItem.innerHTML = "";
const profileDiv = document.createElement("div");
profileDiv.className = "pxme-profile";
const trigger = document.createElement("button");
trigger.className = "pxme-profile__trigger";
trigger.setAttribute("aria-expanded", "false");
trigger.setAttribute("aria-haspopup", "true");
const avatar = document.createElement("img");
avatar.className = "pxme-profile__avatar";
avatar.src = session.user.image || "/img/profile-user.png";
avatar.alt = "Profile";
avatar.width = 40;
avatar.height = 40;
trigger.appendChild(avatar);
const dropdown = document.createElement("div");
dropdown.className = "pxme-profile__dropdown";
dropdown.hidden = true;
dropdown.innerHTML = html`
${this._esc(session.user.name || "")}
${this._esc(session.user.email || "")}
`;
trigger.addEventListener("click", (e) => {
e.stopPropagation();
const open = !dropdown.hidden;
dropdown.hidden = open;
trigger.setAttribute("aria-expanded", String(!open));
});
document.addEventListener("click", () => {
dropdown.hidden = true;
trigger.setAttribute("aria-expanded", "false");
});
// Sign out via POST
dropdown.querySelector("#signout-btn").addEventListener("click", async () => {
await fetch("/auth/signout", { method: "POST" });
window.location.href = "/";
});
profileDiv.appendChild(trigger);
profileDiv.appendChild(dropdown);
loginItem.appendChild(profileDiv);
if (await isAdmin()) {
const adminLi = document.createElement("li");
adminLi.className = "pxme-nav__item";
const adminLink = document.createElement("a");
adminLink.href = "/admin.html";
adminLink.textContent = "Admin";
adminLink.className = "pxme-nav__link";
adminLink.dataset.page = "admin";
adminLi.appendChild(adminLink);
navLinks.insertBefore(adminLi, loginItem);
const facesLi = document.createElement("li");
facesLi.className = "pxme-nav__item";
const facesLink = document.createElement("a");
facesLink.href = "/faces.html";
facesLink.textContent = "Faces";
facesLink.className = "pxme-nav__link";
facesLink.dataset.page = "faces";
facesLi.appendChild(facesLink);
navLinks.insertBefore(facesLi, loginItem);
this._updateActive();
}
return;
}
const signInLink = this.shadowRoot.querySelector('#login-item a[href="/auth/signin"]');
if (signInLink) {
signInLink.addEventListener("click", async (event) => {
event.preventDefault();
const popup = openAuthPopup();
if (!popup) {
signInLink.textContent = "Popup blocked, redirecting...";
fallbackToRedirect();
return;
}
signInLink.textContent = "Waiting for sign-in...";
signInLink.setAttribute("aria-disabled", "true");
try {
await waitForAuthPopup(popup);
window.location.reload();
} catch (error) {
const code = error instanceof Error ? error.message : "auth_error";
signInLink.textContent = "Sign In";
signInLink.removeAttribute("aria-disabled");
if (code === "popup_blocked") {
signInLink.textContent = "Popup blocked, redirecting...";
fallbackToRedirect();
return;
}
if (code === "auth_timeout") {
signInLink.textContent = "Redirecting to sign-in...";
fallbackToRedirect();
}
}
});
}
}
_esc(str) {
return escapeHtml(str);
}
}
customElements.define("pxme-header", PxmeHeader);