photo_server_json_flutter_c.../public/js/main.js
2026-03-05 17:07:30 +01:00

293 lines
No EOL
9.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ===============================
// AVVIO
// ===============================
console.log("main.js avviato");
// ===============================
// UTILS AUTH + SYNC HEADER UI
// ===============================
function isAuthenticated() {
// Fonte verità: presenza del token in localStorage
return !!localStorage.getItem("token");
}
/**
* Sincronizza lUI dellheader in base allo stato auth:
* - Aggiunge una classe sul body (CSS-friendly)
* - Mostra/Nasconde il bottone logout (hotfix inline, se vuoi puoi affidarti solo al CSS)
*/
function syncHeaderAuthUI() {
const authed = isAuthenticated();
document.body.classList.toggle('authenticated', authed);
const logoutBtn = document.getElementById('logoutBtn');
if (logoutBtn) {
// Hotfix immediato: forza la visibilità anche via inline style
// (puoi rimuovere queste due righe se preferisci usare solo la regola CSS body.authenticated #logoutBtn)
logoutBtn.style.display = authed ? 'inline-flex' : 'none';
}
}
// ===============================
// PATCH: misura l'altezza reale dell'header e aggiorna --header-h
// (serve per far partire la mappa subito sotto lheader, anche su mobile)
// ===============================
(function () {
const root = document.documentElement;
const header = document.querySelector('header');
function setHeaderHeight() {
const h = header ? Math.round(header.getBoundingClientRect().height) : 60;
root.style.setProperty('--header-h', h + 'px');
}
setHeaderHeight();
if (window.ResizeObserver && header) {
const ro = new ResizeObserver(setHeaderHeight);
ro.observe(header);
} else {
window.addEventListener('resize', setHeaderHeight);
window.addEventListener('orientationchange', setHeaderHeight);
}
window.addEventListener('load', setHeaderHeight);
})();
// ===============================
// PATCH: quando si apre la mappa (#globalMap.open) invalida le dimensioni Leaflet
// (utile perché prima era display:none; invalidateSize evita “tagli” o tile sfasati)
// ===============================
(function () {
const mapEl = document.getElementById('globalMap');
if (!mapEl) return;
function invalidateWhenOpen() {
if (!mapEl.classList.contains('open')) return;
// Aspetta un tick così il layout è aggiornato
setTimeout(() => {
try {
// In mapGlobal.js imposta: window.leafletMapInstance = window.globalMap;
window.leafletMapInstance?.invalidateSize();
} catch (e) {
console.warn('invalidateSize non eseguito:', e);
}
}, 0);
}
// 1) Osserva il cambio classe (quando aggiungi .open)
const mo = new MutationObserver((mutations) => {
if (mutations.some(m => m.attributeName === 'class')) {
invalidateWhenOpen();
}
});
mo.observe(mapEl, { attributes: true, attributeFilter: ['class'] });
// 2) Fallback: se usi il bottone #openMapBtn per aprire/chiudere
document.getElementById('openMapBtn')?.addEventListener('click', () => {
setTimeout(invalidateWhenOpen, 0);
});
})();
// ===============================
// MENU ⋮ (toggle apri/chiudi con lo stesso bottone, senza global conflicts)
// ===============================
(() => {
const optBtn = document.getElementById("optionsBtn");
const optSheet = document.getElementById("optionsSheet");
const overlayEl= document.getElementById("sheetOverlay");
if (!optBtn || !optSheet) return;
function openOptionsSheet() {
try { window.closeBottomSheet?.(); } catch {}
optSheet.classList.add("open");
overlayEl?.classList.add("open");
// ARIA (facoltativo)
optBtn.setAttribute("aria-expanded", "true");
optSheet.setAttribute("aria-hidden", "false");
}
function closeOptionsSheet() {
optSheet.classList.remove("open");
overlayEl?.classList.remove("open");
// ARIA (facoltativo)
optBtn.setAttribute("aria-expanded", "false");
optSheet.setAttribute("aria-hidden", "true");
}
function toggleOptionsSheet(e) {
e?.preventDefault();
e?.stopPropagation();
if (optSheet.classList.contains("open")) closeOptionsSheet();
else openOptionsSheet();
}
// Click sul bottone: toggle (fase di cattura per battere eventuali altri handler)
optBtn.addEventListener("click", toggleOptionsSheet, { capture: true });
// Chiudi clic overlay
overlayEl?.addEventListener("click", (e) => {
e.stopPropagation();
closeOptionsSheet();
});
// Chiudi con ESC
document.addEventListener("keydown", (e) => {
if (e.key === "Escape" && optSheet.classList.contains("open")) {
closeOptionsSheet();
}
});
// Evita chiusure involontarie per click interni
optSheet.addEventListener("click", (e) => e.stopPropagation());
// Espone una close per usarla altrove (es. dopo la scelta)
window.closeOptionsSheet = closeOptionsSheet;
})();
// ===============================
// LOGIN AUTOMATICO SU INDEX
// ===============================
document.addEventListener("DOMContentLoaded", async () => {
// Allinea subito lUI in base al token eventualmente già presente
syncHeaderAuthUI();
try {
// 1) Carica config
const cfgRes = await fetch('/config');
const cfg = await cfgRes.json();
window.BASE_URL = cfg.baseUrl;
// 2) Recupera token salvato
const savedToken = localStorage.getItem("token");
// Se non c'è token → mostra login
if (!savedToken) {
document.getElementById("loginModal").style.display = "flex";
return;
}
// 3) Verifica token
const ping = await fetch(`${window.BASE_URL}/photos`, {
headers: { "Authorization": "Bearer " + savedToken }
});
if (!ping.ok) {
// Token invalido → cancella e mostra login
localStorage.removeItem("token");
syncHeaderAuthUI(); // riallinea header subito
document.getElementById("loginModal").style.display = "flex";
return;
}
// 4) Token valido → salva e carica gallery
window.token = savedToken;
syncHeaderAuthUI(); // <— mostra il logout senza refresh
loadPhotos();
} catch (err) {
console.error("Errore autenticazione:", err);
document.getElementById("loginModal").style.display = "flex";
}
});
// ===============================
// VARIABILI GLOBALI
// ===============================
let currentSort = "desc";
let currentGroup = "auto";
let currentFilter = null;
window.currentSort = currentSort;
window.currentGroup = currentGroup;
window.currentFilter = currentFilter;
// ===============================
// BOTTONI OPZIONI
// ===============================
document.querySelectorAll("#optionsSheet .sheet-btn").forEach(btn => {
btn.addEventListener("click", () => {
if (btn.dataset.sort) window.currentSort = currentSort = btn.dataset.sort;
if (btn.dataset.group) window.currentGroup = currentGroup = btn.dataset.group;
if (btn.dataset.filter) window.currentFilter = currentFilter = btn.dataset.filter;
// Chiudi sheet e overlay dopo la scelta (usa lAPI esposta sopra)
window.closeOptionsSheet?.();
refreshGallery();
});
});
// ===============================
// REFRESH GALLERY
// ===============================
function refreshGallery() {
console.log("Aggiornamento galleria...");
const data = Array.isArray(window.photosData) ? window.photosData : [];
let photos = [...data];
if (typeof applyFilters === 'function') photos = applyFilters(photos);
if (typeof sortByDate === 'function') photos = sortByDate(photos, currentSort);
let sections = [{ label: 'Tutte', photos }];
if (typeof groupByDate === 'function') sections = groupByDate(photos, currentGroup);
if (typeof renderGallery === 'function') {
renderGallery(sections);
}
}
window.refreshGallery = refreshGallery;
// ===============================
// SETTINGS (⚙️) — apre admin.html
// ===============================
const settingsBtn = document.getElementById('settingsBtn');
settingsBtn?.addEventListener('click', () => {
window.location.href = "admin.html";
});
// ===============================
// LOGIN SUBMIT
// ===============================
document.getElementById("loginSubmit").addEventListener("click", async () => {
const email = document.getElementById("loginEmail").value;
const password = document.getElementById("loginPassword").value;
const errorEl = document.getElementById("loginError");
errorEl.textContent = "";
try {
const res = await fetch(`${window.BASE_URL}/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password })
});
if (!res.ok) {
errorEl.textContent = "Utente o password errati";
return;
}
const data = await res.json();
const token = data.token;
// Salva token
localStorage.setItem("token", token);
window.token = token;
// Chiudi login
const loginModalEl = document.getElementById("loginModal");
if (loginModalEl) loginModalEl.style.display = "none";
// Riallinea UI header subito (mostra logout) e carica gallery
syncHeaderAuthUI();
loadPhotos();
} catch (err) {
console.error("Errore login:", err);
errorEl.textContent = "Errore di connessione al server";
}
});