/** * ========================================================= * Logica Lightbox + Commenti + AI per NETVET (WordPress) * ========================================================= * * Istruzioni di Installazione: * 1. Carica questo file JS nel tuo footer di WordPress (enqueue script). * 2. Assicurati che le variabili globali (__app_id, __firebase_config, * __initial_auth_token) siano disponibili nel contesto del tuo ambiente, * o usa i valori di fallback se stai testando localmente. * 3. Assicurati che netvet-lightbox.css sia caricato. */ (function() { // — 1. CONFIGURAZIONE E INIZIALIZZAZIONE — // Variabili Globali fornite dall’ambiente Canvas/WordPress (modifica i fallback se necessario) const appId = typeof __app_id !== ‘undefined’ ? __app_id : ‘netvet-default-app’; const firebaseConfig = typeof __firebase_config !== ‘undefined’ ? JSON.parse(__firebase_config) : {}; const initialAuthToken = typeof __initial_auth_token !== ‘undefined’ ? __initial_auth_token : null; const GEMINI_MODEL = ‘gemini-2.5-flash-preview-09-2025’; let db, auth, userId = null; let isAuthReady = false; let currentPostId = null; let commentListenerUnsubscribe = null; let lightbox; let lightboxImage; let postTitleElement; let postIdDisplay; let commentsList; let authorInput; let commentTextarea; let submitBtn; let commentErrorMessage; let aiStatusMessage; // — 2. HTML LIGHTBOX (INIETTAZIONE) — // Inietteremo il markup Lightbox nel DOM all’avvio const lightboxHTML = ` `; // — 3. FUNZIONI DI UTILITÀ E API — // Wrapper per l’importazione dinamica di Firebase const loadFirebase = () => { return Promise.all([ import(“https://www.gstatic.com/firebasejs/11.6.1/firebase-app.js”).then(m => m.initializeApp), import(“https://www.gstatic.com/firebasejs/11.6.1/firebase-auth.js”).then(m => ({ getAuth: m.getAuth, signInAnonymously: m.signInAnonymously, signInWithCustomToken: m.signInWithCustomToken, onAuthStateChanged: m.onAuthStateChanged })), import(“https://www.gstatic.com/firebasejs/11.6.1/firebase-firestore.js”).then(m => ({ getFirestore: m.getFirestore, collection: m.collection, query: m.query, where: m.where, orderBy: m.orderBy, onSnapshot: m.onSnapshot, addDoc: m.addDoc, serverTimestamp: m.serverTimestamp })) ]); }; const initFirebase = async () => { try { // Importa i moduli necessari const [initializeApp, authModules, firestoreModules] = await loadFirebase(); const app = initializeApp(firebaseConfig); db = firestoreModules.getFirestore(app); auth = authModules.getAuth(app); // Gestione dell’autenticazione authModules.onAuthStateChanged(auth, async (user) => { if (user) { userId = user.uid; isAuthReady = true; } else { if (initialAuthToken) { await authModules.signInWithCustomToken(auth, initialAuthToken); } else { await authModules.signInAnonymously(auth); } } if (!userId) { userId = auth.currentUser?.uid || crypto.randomUUID(); } isAuthReady = true; }); } catch (error) { console.error(“Errore nell’inizializzazione di Firebase:”, error); } }; const callGeminiAPI = async (systemPrompt, userQuery) => { const apiKey = “”; const apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/${GEMINI_MODEL}:generateContent?key=${apiKey}`; const payload = { contents: [{ parts: [{ text: userQuery }] }], systemInstruction: { parts: [{ text: systemPrompt }] }, }; const maxRetries = 5; for (let i = 0; i < maxRetries; i++) { try { const response = await fetch(apiUrl, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload) }); if (!response.ok) { if (response.status === 429 && i < maxRetries - 1) { const delay = Math.pow(2, i) * 1000 + Math.random() * 1000; await new Promise(resolve => setTimeout(resolve, delay)); continue; } throw new Error(`API call failed with status: ${response.status}`); } const result = await response.json(); const text = result.candidates?.[0]?.content?.parts?.[0]?.text; if (text) { return text; } else { throw new Error(“API response was valid but contained no text content.”); } } catch (error) { console.error(“Errore durante la chiamata a Gemini API:”, error); if (i === maxRetries – 1) { throw new Error(“Fallito il tentativo di comunicazione con l’AI dopo diversi tentativi.”); } } } }; // — 4. FUNZIONI DI INTERAZIONE DEL LIGHTBOX — const checkFormValidity = () => { const author = authorInput.value; const content = commentTextarea.value; submitBtn.disabled = !(author.trim() && content.trim()); }; window.handleCommentSubmission = async () => { const author = authorInput.value; const content = commentTextarea.value; if (!isAuthReady) { commentErrorMessage.textContent = “Attendi la connessione al database. Riprova tra un attimo.”; return; } if (!content.trim() || !author.trim()) { commentErrorMessage.textContent = “Per favor, compila tutti i campi.”; return; } commentErrorMessage.textContent = “”; // Usiamo i moduli Firebase importati const firestoreModules = await import(“https://www.gstatic.com/firebasejs/11.6.1/firebase-firestore.js”); const commentsCollectionPath = `/artifacts/${appId}/public/data/post_comments`; try { await firestoreModules.addDoc(firestoreModules.collection(db, commentsCollectionPath), { postId: currentPostId, authorId: userId, authorName: author.trim(), content: content.trim(), timestamp: firestoreModules.serverTimestamp(), }); authorInput.value = ”; commentTextarea.value = ”; submitBtn.disabled = true; } catch (e) { console.error(“Errore nell’aggiungere il commento: “, e); commentErrorMessage.textContent = “Si è verificato un errore durante l’invio. Riprova.”; } }; window.loadComments = async (postId) => { commentsList.innerHTML = ‘
‘; if (!db || !isAuthReady) { setTimeout(() => window.loadComments(postId), 300); return; } const firestoreModules = await import(“https://www.gstatic.com/firebasejs/11.6.1/firebase-firestore.js”); const { collection, query, where, orderBy, onSnapshot } = firestoreModules; const commentsCollectionPath = `/artifacts/${appId}/public/data/post_comments`; const q = query( collection(db, commentsCollectionPath), where(“postId”, “==”, postId), orderBy(“timestamp”, “desc”) ); const unsubscribe = onSnapshot(q, (querySnapshot) => { let html = ”; if (querySnapshot.empty) { html = ‘

Ancora nessun commento. Scrivine uno!

‘; } else { querySnapshot.forEach((doc) => { const data = doc.data(); const content = data.content.trim(); const date = data.timestamp ? new Date(data.timestamp.toDate()).toLocaleDateString(‘it-IT’) : ‘In attesa…’; let aiButton = ”; if (content.length > 200) { // Usa la classe gemini-purple definita nel CSS se Tailwind non è globale aiButton = ``; } html += `

${data.authorName} ${date} ${aiButton}

${content}

`; }); } commentsList.innerHTML = html; }, (error) => { console.error(“Errore in onSnapshot: “, error); commentsList.innerHTML = `

Errore nel caricamento dei commenti.

`; }); return unsubscribe; }; window.summarizeComment = async (encodedContent, commentId) => { const content = decodeURIComponent(encodedContent); const summaryDiv = document.getElementById(`summary-${commentId}`); summaryDiv.classList.remove(‘hidden’); summaryDiv.innerHTML = ` Riassunto in corso…`; try { const systemPrompt = “Sei un assistente AI per un blog. Il tuo compito è riassumere i commenti degli utenti in una singola frase concisa in italiano, mantenendo un tono neutrale.”; const userQuery = `Riassumi il seguente commento: “${content}”`; const summary = await callGeminiAPI(systemPrompt, userQuery); summaryDiv.innerHTML = `Riassunto AI: ${summary}`; } catch (error) { summaryDiv.innerHTML = `Errore AI: Impossibile generare il riassunto.`; } }; window.generateReplySuggestion = async () => { const btn = document.getElementById(‘generate-reply-btn’); const textarea = commentTextarea; const statusDiv = aiStatusMessage; const commentsContainer = commentsList; const lastComment = commentsContainer.querySelector(‘.comment-item p.text-gray-700:first-child’)?.textContent || “Nessun commento disponibile.”; const postTitle = postTitleElement.textContent; btn.disabled = true; statusDiv.innerHTML = ` Generazione suggerimento…`; statusDiv.classList.remove(‘hidden’, ‘text-red-500’, ‘text-green-500’); statusDiv.classList.add(‘text-gray-600’, ‘flex’, ‘items-center’, ‘mb-2’); try { const systemPrompt = `Sei un moderatore di un blog. Devi scrivere un breve suggerimento di risposta (massimo due frasi in italiano) all’ultimo commento. Il tuo tono deve essere amichevole e incoraggiante. Rispondi solo con il testo del suggerimento.`; const userQuery = `Titolo del post: ${postTitle}. Ultimo commento: “${lastComment}”. Genera una risposta.`; const replySuggestion = await callGeminiAPI(systemPrompt, userQuery); textarea.value = replySuggestion.trim(); statusDiv.innerHTML = `Suggerimento AI caricato! Modifica pure prima di inviare.`; statusDiv.classList.remove(‘text-gray-600’); statusDiv.classList.add(‘text-green-600’); checkFormValidity(); } catch (error) { statusDiv.innerHTML = `Errore AI: Impossibile generare la risposta.`; statusDiv.classList.remove(‘text-gray-600’); statusDiv.classList.add(‘text-red-500’); } finally { btn.disabled = false; } }; window.openLightbox = (imageUrl, postId, postTitle) => { currentPostId = postId; lightboxImage.src = imageUrl; postTitleElement.textContent = postTitle; postIdDisplay.textContent = postId; lightbox.classList.add(‘active’); document.body.style.overflow = ‘hidden’; if (window.loadComments) { if (commentListenerUnsubscribe) { commentListenerUnsubscribe(); } commentListenerUnsubscribe = window.loadComments(postId); } // Reset form e stato AI authorInput.value = ”; commentTextarea.value = ”; commentErrorMessage.textContent = “”; aiStatusMessage.classList.add(‘hidden’); // Assicurati che non sia in modalità fullscreen quando si apre if (document.fullscreenElement) { document.exitFullscreen(); } updateFullscreenIcon(false); checkFormValidity(); } window.closeLightbox = (event) => { if (event && event.target.closest(‘.lightbox-content-wrapper’)) { return; } if (commentListenerUnsubscribe) { commentListenerUnsubscribe(); commentListenerUnsubscribe = null; } if (document.fullscreenElement) { document.exitFullscreen(); } lightbox.classList.remove(‘active’); document.body.style.overflow = ‘auto’; currentPostId = null; } const updateFullscreenIcon = (isEntering) => { const iconElement = document.getElementById(‘fullscreen-icon’); if (!iconElement) return; // Icone SVG per uscire/entrare da fullscreen const exitPath = “M5 13H1v4m0 0h4m-4 0l5-5m7 5h4v-4m0 0h-4m4 0l-5 5m5-18h-4v4m0 0h4m-4 0l5 5m-9-9H5v4m0 0H1m4 0l5 5”; const enterPath = “M4 8V4m0 0h4M4 4l5 5m11-5h-4m4 0v4m0-4l-5 5M4 16v4m0 0h4m-4 0l5-5m11 5l-5-5m5 5v-4m0 4h-4”; if (isEntering) { iconElement.querySelector(‘path’).setAttribute(‘d’, exitPath); } else { iconElement.querySelector(‘path’).setAttribute(‘d’, enterPath); } }; window.toggleFullscreen = () => { if (document.fullscreenElement) { document.exitFullscreen(); } else { if (lightbox.requestFullscreen) { lightbox.requestFullscreen(); } else if (lightbox.webkitRequestFullscreen) { lightbox.webkitRequestFullscreen(); } else if (lightbox.msRequestFullscreen) { lightbox.msRequestFullscreen(); } } }; // — 5. INITIALIZZAZIONE DOM E ASCOLTATORI — document.addEventListener(‘DOMContentLoaded’, () => { // Inietta il Lightbox nel body document.body.insertAdjacentHTML(‘beforeend’, lightboxHTML); // Cattura gli elementi una volta iniettati lightbox = document.getElementById(‘lightbox’); lightboxImage = document.getElementById(‘lightbox-image’); postTitleElement = document.getElementById(‘post-title’); postIdDisplay = document.getElementById(‘post-id-display’); commentsList = document.getElementById(‘comments-list’); authorInput = document.getElementById(‘author-input’); commentTextarea = document.getElementById(‘comment-textarea’); submitBtn = document.getElementById(‘submit-btn’); commentErrorMessage = document.getElementById(‘comment-error-message’); aiStatusMessage = document.getElementById(‘ai-status-message’); // Ascoltatori per l’attivazione del Lightbox (Cattura l’evento su qualsiasi trigger) document.addEventListener(‘click’, (event) => { const target = event.target; if (target.classList.contains(‘lightbox-trigger’)) { event.preventDefault(); const imageUrl = target.src; const postId = target.getAttribute(‘data-post-id’); const postTitle = target.getAttribute(‘data-post-title’) || document.title; // Usa il titolo della pagina come fallback if (postId && imageUrl) { window.openLightbox(imageUrl, postId, postTitle); } else { console.error(“Attributi lightbox-trigger mancanti (data-post-id e src).”); } } }); // Ascoltatori per la validazione del form authorInput.addEventListener(‘input’, checkFormValidity); commentTextarea.addEventListener(‘input’, checkFormValidity); // Ascoltatore per tasto ESC document.addEventListener(‘keydown’, function(e) { if (e.key === ‘Escape’ && lightbox.classList.contains(‘active’)) { if (document.fullscreenElement) { document.exitFullscreen(); } else { window.closeLightbox(); } } }); // Ascoltatore per i cambiamenti di fullscreen document.addEventListener(‘fullscreenchange’, () => { if (!document.fullscreenElement) { lightbox.classList.remove(‘fullscreen-active’); updateFullscreenIcon(false); } else { lightbox.classList.add(‘fullscreen-active’); updateFullscreenIcon(true); } }); // Inizializza Firebase initFirebase(); }); })(); // Nota: Le funzioni globali (window.openLightbox, window.closeLightbox, etc.) sono // necessarie per l’onclick inline del markup HTML e per l’interazione tra i moduli.
Torna in alto