diff --git a/.idea/dataSources.local.xml b/.idea/dataSources.local.xml index 55ed6502..c7ecf9f2 100644 --- a/.idea/dataSources.local.xml +++ b/.idea/dataSources.local.xml @@ -1,6 +1,6 @@ - + #@ diff --git a/MovieVerse-Frontend/js/chat.js b/MovieVerse-Frontend/js/chat.js index 25fecf0e..8470a03d 100644 --- a/MovieVerse-Frontend/js/chat.js +++ b/MovieVerse-Frontend/js/chat.js @@ -1,4 +1,4 @@ -import { initializeApp } from 'https://www.gstatic.com/firebasejs/10.8.0/firebase-app.js'; +import { initializeApp } from "https://www.gstatic.com/firebasejs/10.8.0/firebase-app.js"; import { getFirestore, collection, @@ -13,86 +13,93 @@ import { documentId, serverTimestamp, limit, -} from 'https://www.gstatic.com/firebasejs/10.8.0/firebase-firestore.js'; - -document.addEventListener('DOMContentLoaded', () => { - const mainElement = document.getElementById('main'); - const isLoggedIn = localStorage.getItem('isSignedIn'); - - if (!isLoggedIn || isLoggedIn !== 'true') { - mainElement.style.display = 'none'; - - const signInMessage = document.createElement('div'); - signInMessage.innerHTML = '

You must be signed in to access MovieVerse chat services.

'; - signInMessage.style.display = 'flex'; - signInMessage.style.justifyContent = 'center'; - signInMessage.style.alignItems = 'center'; - signInMessage.style.height = '100vh'; - signInMessage.style.borderRadius = '12px'; - signInMessage.style.margin = '10px auto'; - signInMessage.style.marginRight = '20px'; - signInMessage.style.marginLeft = '20px'; - signInMessage.style.marginBottom = '20px'; - signInMessage.style.backgroundColor = 'rgba(0, 0, 0, 0.6)'; - - const adContainer2 = document.getElementById('ad-container2'); +} from "https://www.gstatic.com/firebasejs/10.8.0/firebase-firestore.js"; + +document.addEventListener("DOMContentLoaded", () => { + const mainElement = document.getElementById("main"); + const isLoggedIn = localStorage.getItem("isSignedIn"); + + if (!isLoggedIn || isLoggedIn !== "true") { + mainElement.style.display = "none"; + + const signInMessage = document.createElement("div"); + signInMessage.innerHTML = + '

You must be signed in to access MovieVerse chat services.

'; + signInMessage.style.display = "flex"; + signInMessage.style.justifyContent = "center"; + signInMessage.style.alignItems = "center"; + signInMessage.style.height = "100vh"; + signInMessage.style.borderRadius = "12px"; + signInMessage.style.margin = "10px auto"; + signInMessage.style.marginRight = "20px"; + signInMessage.style.marginLeft = "20px"; + signInMessage.style.marginBottom = "20px"; + signInMessage.style.backgroundColor = "rgba(0, 0, 0, 0.6)"; + + const adContainer2 = document.getElementById("ad-container2"); if (adContainer2) { document.body.insertBefore(signInMessage, adContainer2); } else { document.body.appendChild(signInMessage); } } else { - mainElement.style.display = ''; + mainElement.style.display = ""; loadUserList(); setupSearchListeners(); } }); async function animateLoadingDots() { - const loadingTextElement = document.querySelector('#myModal p'); - let dots = ''; + const loadingTextElement = document.querySelector("#myModal p"); + let dots = ""; - while (document.getElementById('myModal').classList.contains('modal-visible')) { + while ( + document.getElementById("myModal").classList.contains("modal-visible") + ) { loadingTextElement.textContent = `Loading chats${dots}`; - dots = dots.length < 3 ? dots + '.' : '.'; - await new Promise(resolve => setTimeout(resolve, 500)); + dots = dots.length < 3 ? dots + "." : "."; + await new Promise((resolve) => setTimeout(resolve, 500)); } } const firebaseConfig = { - apiKey: atob('QUl6YVN5REw2a1FuU2ZVZDhVdDhIRnJwS3VpdnF6MXhkWG03aw=='), - authDomain: atob('bW92aWV2ZXJzZS1hcHAuZmlyZWJhc2VhcHAuY29t'), - projectId: 'movieverse-app', - storageBucket: atob('bW92aWV2ZXJzZS1hcHAuYXBwc3BvdC5jb20='), - messagingSenderId: atob('ODAyOTQzNzE4ODcx'), - appId: atob('MTo4MDI5NDM3MTg4NzE6d2ViOjQ4YmM5MTZjYzk5ZTI3MjQyMTI3OTI='), + apiKey: atob("QUl6YVN5REw2a1FuU2ZVZDhVdDhIRnJwS3VpdnF6MXhkWG03aw=="), + authDomain: atob("bW92aWV2ZXJzZS1hcHAuZmlyZWJhc2VhcHAuY29t"), + projectId: "movieverse-app", + storageBucket: atob("bW92aWV2ZXJzZS1hcHAuYXBwc3BvdC5jb20="), + messagingSenderId: atob("ODAyOTQzNzE4ODcx"), + appId: atob("MTo4MDI5NDM3MTg4NzE6d2ViOjQ4YmM5MTZjYzk5ZTI3MjQyMTI3OTI="), }; initializeApp(firebaseConfig); const db = getFirestore(); -const currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); +const currentUserEmail = localStorage.getItem( + "currentlySignedInMovieVerseUser", +); let selectedUserEmail = null; -const messagesDiv = document.getElementById('messages'); -const userListDiv = document.getElementById('userList'); -const messageInput = document.getElementById('messageInput'); -const sendButton = document.getElementById('sendButton'); +const messagesDiv = document.getElementById("messages"); +const userListDiv = document.getElementById("userList"); +const messageInput = document.getElementById("messageInput"); +const sendButton = document.getElementById("sendButton"); -sendButton.addEventListener('click', async () => { +sendButton.addEventListener("click", async () => { const text = messageInput.value.trim(); if (text && selectedUserEmail) { try { - await addDoc(collection(db, 'messages'), { + await addDoc(collection(db, "messages"), { sender: currentUserEmail, recipient: selectedUserEmail, message: text, timestamp: serverTimestamp(), readBy: [currentUserEmail], }); - messageInput.value = ''; + messageInput.value = ""; - const userElement = document.querySelector(`.user[data-email="${selectedUserEmail}"]`); + const userElement = document.querySelector( + `.user[data-email="${selectedUserEmail}"]`, + ); if (!userElement) { const newUserElement = await createUserElement(selectedUserEmail); @@ -103,34 +110,37 @@ sendButton.addEventListener('click', async () => { selectUser(userElement); } } catch (error) { - console.error('Error adding message: ', error); + console.error("Error adding message: ", error); } } }); async function createUserElement(email) { - const userElement = document.createElement('div'); - userElement.classList.add('user'); - userElement.setAttribute('data-email', email); - userElement.addEventListener('click', () => loadMessages(email)); - - const profileQuery = query(collection(db, 'profiles'), where('__name__', '==', email)); + const userElement = document.createElement("div"); + userElement.classList.add("user"); + userElement.setAttribute("data-email", email); + userElement.addEventListener("click", () => loadMessages(email)); + + const profileQuery = query( + collection(db, "profiles"), + where("__name__", "==", email), + ); const profileSnapshot = await getDocs(profileQuery); - let imageUrl = '../../images/user-default.png'; + let imageUrl = "../../images/user-default.png"; if (!profileSnapshot.empty) { const profileData = profileSnapshot.docs[0].data(); imageUrl = profileData.profileImage || imageUrl; } - const img = document.createElement('img'); + const img = document.createElement("img"); img.src = imageUrl; - img.style.width = '50px'; - img.style.borderRadius = '25px'; - img.style.marginRight = '10px'; + img.style.width = "50px"; + img.style.borderRadius = "25px"; + img.style.marginRight = "10px"; userElement.appendChild(img); - const emailDiv = document.createElement('div'); + const emailDiv = document.createElement("div"); emailDiv.textContent = email; userElement.appendChild(emailDiv); @@ -139,57 +149,61 @@ async function createUserElement(email) { function selectUser(userElement) { if (previouslySelectedUserElement) { - previouslySelectedUserElement.classList.remove('selected'); - previouslySelectedUserElement.style.backgroundColor = ''; + previouslySelectedUserElement.classList.remove("selected"); + previouslySelectedUserElement.style.backgroundColor = ""; } - userElement.classList.add('selected'); - userElement.style.backgroundColor = '#ff8623'; + userElement.classList.add("selected"); + userElement.style.backgroundColor = "#ff8623"; previouslySelectedUserElement = userElement; } -document.getElementById('messageInput').addEventListener('keydown', function (event) { - if (event.key === 'Enter' && !event.shiftKey) { - event.preventDefault(); - sendButton.click(); - } -}); +document + .getElementById("messageInput") + .addEventListener("keydown", function (event) { + if (event.key === "Enter" && !event.shiftKey) { + event.preventDefault(); + sendButton.click(); + } + }); function formatMessage(message, isCurrentUser, timestamp) { - const messageElement = document.createElement('div'); - messageElement.classList.add('message'); - messageElement.textContent = isCurrentUser ? `You: ${message}` : `${selectedUserEmail}: ${message}`; + const messageElement = document.createElement("div"); + messageElement.classList.add("message"); + messageElement.textContent = isCurrentUser + ? `You: ${message}` + : `${selectedUserEmail}: ${message}`; if (timestamp && timestamp.toDate) { messageElement.dataset.timestamp = timestamp.toDate().toISOString(); } else { - console.log('Timestamp is not in the expected format:', timestamp); - messageElement.dataset.timestamp = 'Unknown time'; + console.log("Timestamp is not in the expected format:", timestamp); + messageElement.dataset.timestamp = "Unknown time"; } - messageElement.classList.add(isCurrentUser ? 'my-message' : 'other-message'); - messageElement.addEventListener('mouseover', showTooltip); - messageElement.addEventListener('click', showTooltip); + messageElement.classList.add(isCurrentUser ? "my-message" : "other-message"); + messageElement.addEventListener("mouseover", showTooltip); + messageElement.addEventListener("click", showTooltip); return messageElement; } function showTooltip(event) { - const messageElement = event.target.closest('.message'); + const messageElement = event.target.closest(".message"); const timestampString = messageElement.dataset.timestamp; const date = new Date(timestampString); const tooltipText = isNaN(date.getTime()) - ? 'Unknown time' - : date.toLocaleString('default', { - year: 'numeric', - month: 'short', - day: 'numeric', - hour: '2-digit', - minute: '2-digit', + ? "Unknown time" + : date.toLocaleString("default", { + year: "numeric", + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit", }); - const tooltip = document.createElement('div'); - tooltip.className = 'tooltip'; + const tooltip = document.createElement("div"); + tooltip.className = "tooltip"; tooltip.textContent = tooltipText; document.body.appendChild(tooltip); @@ -197,68 +211,124 @@ function showTooltip(event) { const tooltipRect = tooltip.getBoundingClientRect(); const leftPosition = rect.left + rect.width / 2 - tooltipRect.width / 2; - tooltip.style.position = 'fixed'; + tooltip.style.position = "fixed"; tooltip.style.top = `${rect.top - tooltipRect.height - 5}px`; tooltip.style.left = `${Math.max(leftPosition, 0) - 12}px`; function removeTooltip() { tooltip.remove(); } - messageElement.addEventListener('mouseout', removeTooltip); + messageElement.addEventListener("mouseout", removeTooltip); setTimeout(removeTooltip, 5000); } -const chatSection = document.getElementById('chatSection'); -const noUserSelected = document.getElementById('noUserSelected'); +const chatSection = document.getElementById("chatSection"); +const noUserSelected = document.getElementById("noUserSelected"); -chatSection.style.display = 'none'; -noUserSelected.style.display = 'flex'; +chatSection.style.display = "none"; +noUserSelected.style.display = "flex"; + +const LOCAL_STORAGE_MESSAGES_KEY_PREFIX = "movieVerseMessagesCache"; + +function getCachedMessages(conversationKey) { + const cachedData = localStorage.getItem( + LOCAL_STORAGE_MESSAGES_KEY_PREFIX + conversationKey, + ); + return cachedData ? JSON.parse(cachedData) : []; +} + +function updateMessageCache(conversationKey, messages) { + localStorage.setItem( + LOCAL_STORAGE_MESSAGES_KEY_PREFIX + conversationKey, + JSON.stringify(messages), + ); +} + +function clearMessageCache(conversationKey) { + localStorage.removeItem(LOCAL_STORAGE_MESSAGES_KEY_PREFIX + conversationKey); +} async function loadMessages(userEmail) { selectedUserEmail = userEmail; - messagesDiv.innerHTML = ''; + messagesDiv.innerHTML = ""; - chatSection.style.display = 'flex'; - noUserSelected.style.display = 'none'; + chatSection.style.display = "flex"; + noUserSelected.style.display = "none"; - const userEmailDisplay = document.getElementById('userEmailDisplay'); + const userEmailDisplay = document.getElementById("userEmailDisplay"); if (userEmailDisplay) { userEmailDisplay.textContent = `Chatting with: ${selectedUserEmail}`; } - document.querySelectorAll('.user').forEach(user => user.classList.remove('selected')); - const selectedUser = document.querySelector(`.user[data-email="${selectedUserEmail}"]`); + document + .querySelectorAll(".user") + .forEach((user) => user.classList.remove("selected")); + const selectedUser = document.querySelector( + `.user[data-email="${selectedUserEmail}"]`, + ); if (selectedUser) { - selectedUser.classList.add('selected'); + selectedUser.classList.add("selected"); + } + + const conversationKey = `${currentUserEmail}_${selectedUserEmail}`; + + const cachedMessages = getCachedMessages(conversationKey); + if (cachedMessages.length > 0) { + cachedMessages.forEach((msg) => { + const messageElement = formatMessage( + msg.message, + msg.isCurrentUser, + msg.timestamp, + ); + messagesDiv.appendChild(messageElement); + }); + messagesDiv.scrollTop = messagesDiv.scrollHeight; } const messagesQuery = query( - collection(db, 'messages'), - orderBy('timestamp'), - where('sender', 'in', [currentUserEmail, selectedUserEmail]), - where('recipient', 'in', [currentUserEmail, selectedUserEmail]) + collection(db, "messages"), + orderBy("timestamp"), + where("sender", "in", [currentUserEmail, selectedUserEmail]), + where("recipient", "in", [currentUserEmail, selectedUserEmail]), ); - onSnapshot(messagesQuery, snapshot => { - messagesDiv.innerHTML = ''; - snapshot.docs.forEach(doc => { + onSnapshot(messagesQuery, (snapshot) => { + const newMessages = []; + + messagesDiv.innerHTML = ""; + snapshot.docs.forEach((doc) => { const messageData = doc.data(); const isCurrentUser = messageData.sender === currentUserEmail; const timestamp = messageData.timestamp; - const messageElement = formatMessage(messageData.message, isCurrentUser, timestamp); + const messageElement = formatMessage( + messageData.message, + isCurrentUser, + timestamp, + ); messagesDiv.appendChild(messageElement); - if (!isCurrentUser && (!messageData.readBy || !messageData.readBy.includes(currentUserEmail))) { + if ( + !isCurrentUser && + (!messageData.readBy || !messageData.readBy.includes(currentUserEmail)) + ) { updateReadStatus(doc.id); } + + newMessages.push({ + message: messageData.message, + isCurrentUser, + timestamp: timestamp ? timestamp.toDate().toISOString() : null, + }); }); + updateMessageCache(conversationKey, newMessages); + messagesDiv.scrollTop = messagesDiv.scrollHeight; }); } async function updateReadStatus(messageId) { - const messageRef = doc(db, 'messages', messageId); + const messageRef = doc(db, "messages", messageId); await updateDoc(messageRef, { readBy: arrayUnion(currentUserEmail), }); @@ -270,10 +340,10 @@ const initialFetchLimit = 5; const maxTotalFetch = 20; function setupSearchListeners() { - const searchUserInput = document.getElementById('searchUserInput'); - const searchUserResults = document.getElementById('searchUserResults'); + const searchUserInput = document.getElementById("searchUserInput"); + const searchUserResults = document.getElementById("searchUserResults"); - searchUserInput.addEventListener('input', () => { + searchUserInput.addEventListener("input", () => { clearTimeout(searchDebounceTimeout); const searchText = searchUserInput.value.trim(); @@ -283,25 +353,73 @@ function setupSearchListeners() { performSearch(searchText, true); }, 300); } else { - searchUserResults.innerHTML = ''; - searchUserResults.style.display = 'none'; + searchUserResults.innerHTML = ""; + searchUserResults.style.display = "none"; } }); } +const LOCAL_STORAGE_SEARCH_CACHE_KEY = "movieVerseSearchCache"; + +function getCachedSearchResults(query) { + const cachedData = localStorage.getItem(LOCAL_STORAGE_SEARCH_CACHE_KEY); + if (cachedData) { + const searchCache = JSON.parse(cachedData); + return searchCache[query] ? searchCache[query].results : null; + } + return null; +} + +function updateSearchCache(query, results) { + const cachedData = localStorage.getItem(LOCAL_STORAGE_SEARCH_CACHE_KEY); + const searchCache = cachedData ? JSON.parse(cachedData) : {}; + searchCache[query] = { results, lastUpdated: Date.now() }; + localStorage.setItem( + LOCAL_STORAGE_SEARCH_CACHE_KEY, + JSON.stringify(searchCache), + ); +} + async function performSearch(searchText, isNewSearch = false) { - const searchUserResults = document.getElementById('searchUserResults'); + const searchUserResults = document.getElementById("searchUserResults"); + const cachedResults = getCachedSearchResults(searchText); + + if (cachedResults && isNewSearch) { + searchUserResults.innerHTML = ""; + cachedResults.forEach((user) => { + const userDiv = document.createElement("div"); + userDiv.className = "user-search-result"; + userDiv.style.cursor = "pointer"; + userDiv.addEventListener("click", () => loadMessages(user.email)); + + const img = document.createElement("img"); + img.src = user.imageUrl || "../../images/user-default.png"; + img.style.width = "33%"; + img.style.borderRadius = "8px"; + userDiv.appendChild(img); + + const textDiv = document.createElement("div"); + textDiv.style.width = "67%"; + textDiv.style.textAlign = "left"; + textDiv.innerHTML = `${user.email}

${user.bio || ""}

`; + userDiv.appendChild(textDiv); + + searchUserResults.appendChild(userDiv); + }); + searchUserResults.style.display = "block"; + return; + } try { showSpinner(); animateLoadingDots(); let userQuery = query( - collection(db, 'MovieVerseUsers'), - where('email', '>=', searchText), - where('email', '<=', searchText + '\uf8ff'), - orderBy('email'), - limit(initialFetchLimit) + collection(db, "MovieVerseUsers"), + where("email", ">=", searchText), + where("email", "<=", searchText + "\uf8ff"), + orderBy("email"), + limit(initialFetchLimit), ); if (!isNewSearch && lastVisible) { @@ -311,66 +429,78 @@ async function performSearch(searchText, isNewSearch = false) { const querySnapshot = await getDocs(userQuery); if (isNewSearch) { - searchUserResults.innerHTML = ''; + searchUserResults.innerHTML = ""; } if (!querySnapshot.empty) { lastVisible = querySnapshot.docs[querySnapshot.docs.length - 1]; } + const results = []; for (const doc of querySnapshot.docs) { const user = doc.data(); - const userDiv = document.createElement('div'); - userDiv.className = 'user-search-result'; - userDiv.style.cursor = 'pointer'; - userDiv.addEventListener('click', () => loadMessages(user.email)); - - const profileQuery = query(collection(db, 'profiles'), where('__name__', '==', user.email)); + const userDiv = document.createElement("div"); + userDiv.className = "user-search-result"; + userDiv.style.cursor = "pointer"; + userDiv.addEventListener("click", () => loadMessages(user.email)); + + const profileQuery = query( + collection(db, "profiles"), + where("__name__", "==", user.email), + ); const profileSnapshot = await getDocs(profileQuery); - let imageUrl = '../../images/user-default.png'; + let imageUrl = "../../images/user-default.png"; if (!profileSnapshot.empty) { const profileData = profileSnapshot.docs[0].data(); imageUrl = profileData.profileImage || imageUrl; } - const img = document.createElement('img'); + const img = document.createElement("img"); img.src = imageUrl; - img.style.width = '33%'; - img.style.borderRadius = '8px'; + img.style.width = "33%"; + img.style.borderRadius = "8px"; userDiv.appendChild(img); - const textDiv = document.createElement('div'); - textDiv.style.width = '67%'; - textDiv.style.textAlign = 'left'; - textDiv.innerHTML = `${user.email}

${user.bio || ''}

`; + const textDiv = document.createElement("div"); + textDiv.style.width = "67%"; + textDiv.style.textAlign = "left"; + textDiv.innerHTML = `${user.email}

${user.bio || ""}

`; userDiv.appendChild(textDiv); searchUserResults.appendChild(userDiv); + results.push({ email: user.email, bio: user.bio, imageUrl }); } - searchUserResults.style.display = 'block'; + searchUserResults.style.display = "block"; hideSpinner(); - if (isNewSearch || (!querySnapshot.empty && querySnapshot.size === initialFetchLimit)) { - const loadMoreButton = document.createElement('button'); - loadMoreButton.textContent = 'Load More'; - loadMoreButton.id = 'loadMoreButton'; - loadMoreButton.style.marginBottom = '20px'; - loadMoreButton.addEventListener('click', () => performSearch(searchText)); + if ( + isNewSearch || + (!querySnapshot.empty && querySnapshot.size === initialFetchLimit) + ) { + const loadMoreButton = document.createElement("button"); + loadMoreButton.textContent = "Load More"; + loadMoreButton.id = "loadMoreButton"; + loadMoreButton.style.marginBottom = "20px"; + loadMoreButton.addEventListener("click", () => performSearch(searchText)); searchUserResults.appendChild(loadMoreButton); if (searchUserResults.children.length >= maxTotalFetch) { - loadMoreButton.style.display = 'none'; + loadMoreButton.style.display = "none"; } } + + if (isNewSearch) { + updateSearchCache(searchText, results); + } } catch (error) { - console.error('Error fetching user list: ', error); - if (error.code === 'resource-exhausted') { - const noUserSelected = document.getElementById('noUserSelected'); + console.error("Error fetching user list: ", error); + if (error.code === "resource-exhausted") { + const noUserSelected = document.getElementById("noUserSelected"); if (noUserSelected) { noUserSelected.textContent = "Sorry, the chat feature is currently unavailable as our database is overloaded. Please try reloading once more, and if that still doesn't work, please try again in a couple hours. For full transparency, we are currently using a database that has a limited number of reads and writes per day due to lack of funding. Thank you for your patience as we work on scaling our services. At the mean time, feel free to use other MovieVerse features!"; - noUserSelected.style.margin = '25px auto'; + noUserSelected.style.margin = "25px auto"; } hideSpinner(); } @@ -379,6 +509,26 @@ async function performSearch(searchText, isNewSearch = false) { let previouslySelectedUserElement = null; +const LOCAL_STORAGE_USER_CACHE_KEY = "movieVerseUserCache"; + +function getCachedUsers() { + const cachedData = localStorage.getItem(LOCAL_STORAGE_USER_CACHE_KEY); + return cachedData ? JSON.parse(cachedData) : {}; +} + +function updateUserCache(email, userData) { + const currentCache = getCachedUsers(); + currentCache[email] = userData; + localStorage.setItem( + LOCAL_STORAGE_USER_CACHE_KEY, + JSON.stringify(currentCache), + ); +} + +function clearUserCache() { + localStorage.removeItem(LOCAL_STORAGE_USER_CACHE_KEY); +} + async function loadUserList() { try { showSpinner(); @@ -388,84 +538,129 @@ async function loadUserList() { const messageLimit = 30; const sentMessagesQuery = query( - collection(db, 'messages'), - orderBy('timestamp', 'desc'), - where('sender', '==', currentUserEmail), - limit(messageLimit) + collection(db, "messages"), + orderBy("timestamp", "desc"), + where("sender", "==", currentUserEmail), + limit(messageLimit), ); const receivedMessagesQuery = query( - collection(db, 'messages'), - orderBy('timestamp', 'desc'), - where('recipient', '==', currentUserEmail), - limit(messageLimit) + collection(db, "messages"), + orderBy("timestamp", "desc"), + where("recipient", "==", currentUserEmail), + limit(messageLimit), ); - const [sentMessagesSnapshot, receivedMessagesSnapshot] = await Promise.all([getDocs(sentMessagesQuery), getDocs(receivedMessagesQuery)]); + const [sentMessagesSnapshot, receivedMessagesSnapshot] = await Promise.all([ + getDocs(sentMessagesQuery), + getDocs(receivedMessagesQuery), + ]); let userEmails = new Set(); - sentMessagesSnapshot.forEach(doc => userEmails.add(doc.data().recipient)); - receivedMessagesSnapshot.forEach(doc => userEmails.add(doc.data().sender)); + sentMessagesSnapshot.forEach((doc) => userEmails.add(doc.data().recipient)); + receivedMessagesSnapshot.forEach((doc) => + userEmails.add(doc.data().sender), + ); let users = []; + const cachedUsers = getCachedUsers(); + for (let email of userEmails) { if (email) { - const userQuery = query(collection(db, 'MovieVerseUsers'), where('email', '==', email)); - const userSnapshot = await getDocs(userQuery); - userSnapshot.forEach(doc => { - let userData = doc.data(); - if (userData.email) { - users.push(userData); - } - }); + if ( + cachedUsers[email] && + cachedUsers[email].lastUpdated && + Date.now() - cachedUsers[email].lastUpdated < 24 * 60 * 60 * 1000 + ) { + users.push(cachedUsers[email]); + } else { + const userQuery = query( + collection(db, "MovieVerseUsers"), + where("email", "==", email), + ); + const userSnapshot = await getDocs(userQuery); + userSnapshot.forEach((doc) => { + let userData = doc.data(); + if (userData.email) { + userData.lastUpdated = Date.now(); + updateUserCache(email, userData); + users.push(userData); + } + }); + } } } users.sort((a, b) => { - const aLastMessage = [...sentMessagesSnapshot.docs, ...receivedMessagesSnapshot.docs].find( - doc => doc.data().sender === a.email || doc.data().recipient === a.email + const aLastMessage = [ + ...sentMessagesSnapshot.docs, + ...receivedMessagesSnapshot.docs, + ].find( + (doc) => + doc.data().sender === a.email || doc.data().recipient === a.email, ); - const bLastMessage = [...sentMessagesSnapshot.docs, ...receivedMessagesSnapshot.docs].find( - doc => doc.data().sender === b.email || doc.data().recipient === b.email + const bLastMessage = [ + ...sentMessagesSnapshot.docs, + ...receivedMessagesSnapshot.docs, + ].find( + (doc) => + doc.data().sender === b.email || doc.data().recipient === b.email, + ); + return ( + (bLastMessage?.data().timestamp.toDate() || 0) - + (aLastMessage?.data().timestamp.toDate() || 0) ); - return (bLastMessage?.data().timestamp.toDate() || 0) - (aLastMessage?.data().timestamp.toDate() || 0); }); users = users.slice(0, userLimit); - userListDiv.innerHTML = ''; + userListDiv.innerHTML = ""; for (const user of users) { - const userElement = document.createElement('div'); - userElement.classList.add('user'); - userElement.setAttribute('data-email', user.email); + const userElement = document.createElement("div"); + userElement.classList.add("user"); + userElement.setAttribute("data-email", user.email); userElement.onclick = () => { if (previouslySelectedUserElement) { - previouslySelectedUserElement.classList.remove('selected'); - previouslySelectedUserElement.style.backgroundColor = ''; + previouslySelectedUserElement.classList.remove("selected"); + previouslySelectedUserElement.style.backgroundColor = ""; } selectedUserEmail = user.email; loadMessages(user.email); - document.querySelectorAll('.user').forEach(u => u.classList.remove('selected')); - userElement.classList.add('selected'); - userElement.style.backgroundColor = '#ff8623'; + document + .querySelectorAll(".user") + .forEach((u) => u.classList.remove("selected")); + userElement.classList.add("selected"); + userElement.style.backgroundColor = "#ff8623"; previouslySelectedUserElement = userElement; }; - const profileQuery = query(collection(db, 'profiles'), where('__name__', '==', user.email)); - const profileSnapshot = await getDocs(profileQuery); - let imageUrl = '../../images/user-default.png'; - if (!profileSnapshot.empty) { - const profileData = profileSnapshot.docs[0].data(); - imageUrl = profileData.profileImage || imageUrl; + let imageUrl = "../../images/user-default.png"; + if (cachedUsers[user.email] && cachedUsers[user.email].profileImage) { + imageUrl = cachedUsers[user.email].profileImage; + } else { + const profileQuery = query( + collection(db, "profiles"), + where("__name__", "==", user.email), + ); + const profileSnapshot = await getDocs(profileQuery); + if (!profileSnapshot.empty) { + const profileData = profileSnapshot.docs[0].data(); + imageUrl = profileData.profileImage || imageUrl; + + if (cachedUsers[user.email]) { + cachedUsers[user.email].profileImage = imageUrl; + updateUserCache(user.email, cachedUsers[user.email]); + } + } } - const img = document.createElement('img'); + const img = document.createElement("img"); img.src = imageUrl; - img.style.width = '50px'; - img.style.borderRadius = '25px'; - img.style.marginRight = '10px'; + img.style.width = "50px"; + img.style.borderRadius = "25px"; + img.style.marginRight = "10px"; userElement.appendChild(img); - const emailDiv = document.createElement('div'); + const emailDiv = document.createElement("div"); emailDiv.textContent = user.email; userElement.appendChild(emailDiv); @@ -474,9 +669,9 @@ async function loadUserList() { hideSpinner(); } catch (error) { - console.error('Error fetching user list: ', error); - if (error.code === 'resource-exhausted') { - const noUserSelected = document.getElementById('noUserSelected'); + console.error("Error fetching user list: ", error); + if (error.code === "resource-exhausted") { + const noUserSelected = document.getElementById("noUserSelected"); if (noUserSelected) { noUserSelected.textContent = "Sorry, our database is currently overloaded. Please try reloading once more, and if that still doesn't work, please try again in a couple hours. For full transparency, we are currently using a database that has a limited number of reads and writes per day due to lack of funding. Thank you for your patience as we work on scaling our services. At the mean time, feel free to use other MovieVerse features!"; @@ -487,9 +682,9 @@ async function loadUserList() { } function showSpinner() { - document.getElementById('myModal').classList.add('modal-visible'); + document.getElementById("myModal").classList.add("modal-visible"); } function hideSpinner() { - document.getElementById('myModal').classList.remove('modal-visible'); + document.getElementById("myModal").classList.remove("modal-visible"); } diff --git a/MovieVerse-Frontend/js/chatbot.js b/MovieVerse-Frontend/js/chatbot.js index db8687e8..52085141 100644 --- a/MovieVerse-Frontend/js/chatbot.js +++ b/MovieVerse-Frontend/js/chatbot.js @@ -1,15 +1,19 @@ -import { GoogleGenerativeAI, HarmBlockThreshold, HarmCategory } from '@google/generative-ai'; +import { + GoogleGenerativeAI, + HarmBlockThreshold, + HarmCategory, +} from "@google/generative-ai"; -const chatbotBody = document.getElementById('chatbotBody'); +const chatbotBody = document.getElementById("chatbotBody"); const movieee = `https://${getMovieVerseData()}/3`; let initialMainContent; let conversationHistory = []; const movieCode = { - part1: 'YzVhMjBjODY=', - part2: 'MWFjZjdiYjg=', - part3: 'ZDllOTg3ZGNjN2YxYjU1OA==', + part1: "YzVhMjBjODY=", + part2: "MWFjZjdiYjg=", + part3: "ZDllOTg3ZGNjN2YxYjU1OA==", }; function getMovieCode() { @@ -21,20 +25,39 @@ function generateMovieNames(input) { } function getMovieVerseData(input) { - return String.fromCharCode(97, 112, 105, 46, 116, 104, 101, 109, 111, 118, 105, 101, 100, 98, 46, 111, 114, 103); + return String.fromCharCode( + 97, + 112, + 105, + 46, + 116, + 104, + 101, + 109, + 111, + 118, + 105, + 101, + 100, + 98, + 46, + 111, + 114, + 103, + ); } -document.addEventListener('DOMContentLoaded', function () { - initialMainContent = document.getElementById('main').innerHTML; +document.addEventListener("DOMContentLoaded", function () { + initialMainContent = document.getElementById("main").innerHTML; initializeChatbot(); - document.getElementById('clear-search-btn').style.display = 'none'; + document.getElementById("clear-search-btn").style.display = "none"; }); -const searchTitle = document.getElementById('search-title'); +const searchTitle = document.getElementById("search-title"); const SEARCHPATH = `https://${getMovieVerseData()}/3/search/movie?&${generateMovieNames()}${getMovieCode()}&query=`; async function ensureGenreMapIsAvailable() { - if (!localStorage.getItem('genreMap')) { + if (!localStorage.getItem("genreMap")) { await fetchGenreMap(); } } @@ -48,9 +71,9 @@ async function fetchGenreMap() { map[genre.id] = genre.name; return map; }, {}); - localStorage.setItem('genreMap', JSON.stringify(genreMap)); + localStorage.setItem("genreMap", JSON.stringify(genreMap)); } catch (error) { - console.log('Error fetching genre map:', error); + console.log("Error fetching genre map:", error); } } @@ -59,49 +82,51 @@ async function rotateUserStats() { const stats = [ { - label: 'Your Current Time', + label: "Your Current Time", getValue: () => { const now = new Date(); let hours = now.getHours(); let minutes = now.getMinutes(); - hours = hours < 10 ? '0' + hours : hours; - minutes = minutes < 10 ? '0' + minutes : minutes; + hours = hours < 10 ? "0" + hours : hours; + minutes = minutes < 10 ? "0" + minutes : minutes; return `${hours}:${minutes}`; }, }, - { label: 'Most Visited Movie', getValue: getMostVisitedMovie }, - { label: 'Most Visited Director', getValue: getMostVisitedDirector }, - { label: 'Most Visited Actor', getValue: getMostVisitedActor }, + { label: "Most Visited Movie", getValue: getMostVisitedMovie }, + { label: "Most Visited Director", getValue: getMostVisitedDirector }, + { label: "Most Visited Actor", getValue: getMostVisitedActor }, { - label: 'Movies Discovered', + label: "Movies Discovered", getValue: () => { - const viewedMovies = JSON.parse(localStorage.getItem('uniqueMoviesViewed')) || []; + const viewedMovies = + JSON.parse(localStorage.getItem("uniqueMoviesViewed")) || []; return viewedMovies.length; }, }, { - label: 'Favorite Movies', + label: "Favorite Movies", getValue: () => { - const favoritedMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; + const favoritedMovies = + JSON.parse(localStorage.getItem("moviesFavorited")) || []; return favoritedMovies.length; }, }, { - label: 'Favorite Genre', + label: "Favorite Genre", getValue: () => { const mostCommonGenreCode = getMostCommonGenre(); - const genreMapString = localStorage.getItem('genreMap'); + const genreMapString = localStorage.getItem("genreMap"); if (!genreMapString) { - console.log('No genre map found in localStorage.'); - return 'Not Available'; + console.log("No genre map found in localStorage."); + return "Not Available"; } let genreMap; try { genreMap = JSON.parse(genreMapString); } catch (e) { - console.log('Error parsing genre map:', e); - return 'Not Available'; + console.log("Error parsing genre map:", e); + return "Not Available"; } let genreObject; @@ -110,66 +135,74 @@ async function rotateUserStats() { acc[genre.id] = genre.name; return acc; }, {}); - } else if (typeof genreMap === 'object' && genreMap !== null) { + } else if (typeof genreMap === "object" && genreMap !== null) { genreObject = genreMap; } else { - console.log('genreMap is neither an array nor a proper object:', genreMap); - return 'Not Available'; + console.log( + "genreMap is neither an array nor a proper object:", + genreMap, + ); + return "Not Available"; } - return genreObject[mostCommonGenreCode] || 'Not Available'; + return genreObject[mostCommonGenreCode] || "Not Available"; }, }, { - label: 'Watchlists Created', - getValue: () => localStorage.getItem('watchlistsCreated') || 0, + label: "Watchlists Created", + getValue: () => localStorage.getItem("watchlistsCreated") || 0, }, { - label: 'Average Movie Rating', - getValue: () => localStorage.getItem('averageMovieRating') || 'Not Rated', + label: "Average Movie Rating", + getValue: () => localStorage.getItem("averageMovieRating") || "Not Rated", }, { - label: 'Directors Discovered', + label: "Directors Discovered", getValue: () => { - const viewedDirectors = JSON.parse(localStorage.getItem('uniqueDirectorsViewed')) || []; + const viewedDirectors = + JSON.parse(localStorage.getItem("uniqueDirectorsViewed")) || []; return viewedDirectors.length; }, }, { - label: 'Actors Discovered', + label: "Actors Discovered", getValue: () => { - const viewedActors = JSON.parse(localStorage.getItem('uniqueActorsViewed')) || []; + const viewedActors = + JSON.parse(localStorage.getItem("uniqueActorsViewed")) || []; return viewedActors.length; }, }, - { label: 'Your Trivia Accuracy', getValue: getTriviaAccuracy }, + { label: "Your Trivia Accuracy", getValue: getTriviaAccuracy }, ]; let currentStatIndex = 0; function updateStatDisplay() { const currentStat = stats[currentStatIndex]; - document.getElementById('stats-label').textContent = currentStat.label + ':'; - document.getElementById('stats-display').textContent = currentStat.getValue(); + document.getElementById("stats-label").textContent = + currentStat.label + ":"; + document.getElementById("stats-display").textContent = + currentStat.getValue(); currentStatIndex = (currentStatIndex + 1) % stats.length; } updateStatDisplay(); - const localTimeDiv = document.getElementById('local-time'); + const localTimeDiv = document.getElementById("local-time"); let statRotationInterval = setInterval(updateStatDisplay, 3000); - localTimeDiv.addEventListener('click', () => { + localTimeDiv.addEventListener("click", () => { clearInterval(statRotationInterval); updateStatDisplay(); statRotationInterval = setInterval(updateStatDisplay, 3000); - localTimeDiv.scrollIntoView({ behavior: 'smooth' }); + localTimeDiv.scrollIntoView({ behavior: "smooth" }); }); } function getMostVisitedDirector() { - const directorVisits = JSON.parse(localStorage.getItem('directorVisits')) || {}; - let mostVisitedDirector = ''; + const directorVisits = + JSON.parse(localStorage.getItem("directorVisits")) || {}; + let mostVisitedDirector = ""; let maxVisits = 0; for (const directorId in directorVisits) { @@ -179,12 +212,12 @@ function getMostVisitedDirector() { } } - return mostVisitedDirector || 'Not Available'; + return mostVisitedDirector || "Not Available"; } function getMostVisitedMovie() { - const movieVisits = JSON.parse(localStorage.getItem('movieVisits')) || {}; - let mostVisitedMovie = ''; + const movieVisits = JSON.parse(localStorage.getItem("movieVisits")) || {}; + let mostVisitedMovie = ""; let maxVisits = 0; for (const movieId in movieVisits) { @@ -194,13 +227,13 @@ function getMostVisitedMovie() { } } - return mostVisitedMovie || 'Not Available'; + return mostVisitedMovie || "Not Available"; } function getMostVisitedActor() { - const actorVisits = JSON.parse(localStorage.getItem('actorVisits')) || {}; + const actorVisits = JSON.parse(localStorage.getItem("actorVisits")) || {}; - let mostVisitedActor = ''; + let mostVisitedActor = ""; let maxVisits = 0; for (const actorId in actorVisits) { @@ -210,17 +243,17 @@ function getMostVisitedActor() { } } - return mostVisitedActor || 'Not Available'; + return mostVisitedActor || "Not Available"; } function getTriviaAccuracy() { - let triviaStats = JSON.parse(localStorage.getItem('triviaStats')) || { + let triviaStats = JSON.parse(localStorage.getItem("triviaStats")) || { totalCorrect: 0, totalAttempted: 0, }; if (triviaStats.totalAttempted === 0) { - return 'No trivia attempted'; + return "No trivia attempted"; } let accuracy = (triviaStats.totalCorrect / triviaStats.totalAttempted) * 100; @@ -228,13 +261,14 @@ function getTriviaAccuracy() { } function getMostCommonGenre() { - const favoriteGenresArray = JSON.parse(localStorage.getItem('favoriteGenres')) || []; + const favoriteGenresArray = + JSON.parse(localStorage.getItem("favoriteGenres")) || []; const genreCounts = favoriteGenresArray.reduce((acc, genre) => { acc[genre] = (acc[genre] || 0) + 1; return acc; }, {}); - let mostCommonGenre = ''; + let mostCommonGenre = ""; let maxCount = 0; for (const genre in genreCounts) { @@ -244,25 +278,25 @@ function getMostCommonGenre() { } } - return mostCommonGenre || 'Not Available'; + return mostCommonGenre || "Not Available"; } -document.addEventListener('DOMContentLoaded', rotateUserStats); +document.addEventListener("DOMContentLoaded", rotateUserStats); function initializeChatbot() { - const chatbotInput = document.getElementById('chatbotInput'); + const chatbotInput = document.getElementById("chatbotInput"); sendInitialInstructions(); - chatbotInput.addEventListener('keydown', function (event) { - if (event.key === 'Enter') { + chatbotInput.addEventListener("keydown", function (event) { + if (event.key === "Enter") { sendMessage(chatbotInput.value); - chatbotInput.value = ''; + chatbotInput.value = ""; } }); - const sendButton = document.getElementById('sendButton'); - sendButton.addEventListener('click', function () { + const sendButton = document.getElementById("sendButton"); + sendButton.addEventListener("click", function () { sendMessage(chatbotInput.value); - chatbotInput.value = ''; + chatbotInput.value = ""; }); } @@ -299,18 +333,20 @@ function scrollToBottom() { chatbotBody.scrollTop = chatbotBody.scrollHeight; } -document.getElementById('clear-search-btn').addEventListener('click', function () { - document.getElementById('main').innerHTML = initialMainContent; - initializeChatbot(); - searchTitle.innerHTML = ''; - this.style.display = 'none'; -}); +document + .getElementById("clear-search-btn") + .addEventListener("click", function () { + document.getElementById("main").innerHTML = initialMainContent; + initializeChatbot(); + searchTitle.innerHTML = ""; + this.style.display = "none"; + }); -form.addEventListener('submit', e => { +form.addEventListener("submit", (e) => { e.preventDefault(); - const searchQuery = document.getElementById('search').value; - localStorage.setItem('searchQuery', searchQuery); - window.location.href = 'search.html'; + const searchQuery = document.getElementById("search").value; + localStorage.setItem("searchQuery", searchQuery); + window.location.href = "search.html"; }); async function fetchMovieTrailer(movieName) { @@ -324,13 +360,15 @@ async function fetchMovieTrailer(movieName) { if (trailerUrl) { createTrailerButton(trailerUrl, movie.title); } else { - chatbotBody.innerHTML += '
No trailer available for this movie.
'; + chatbotBody.innerHTML += + "
No trailer available for this movie.
"; } } else { - chatbotBody.innerHTML += '
Movie not found. Please try another search.
'; + chatbotBody.innerHTML += + "
Movie not found. Please try another search.
"; } } catch (error) { - console.log('Error fetching movie trailer:', error); + console.log("Error fetching movie trailer:", error); } } @@ -340,22 +378,24 @@ async function getTrailerUrl(movieId) { try { const response = await fetch(trailerApiUrl); const data = await response.json(); - const trailer = data.results.find(video => video.type === 'Trailer' && video.site === 'YouTube'); + const trailer = data.results.find( + (video) => video.type === "Trailer" && video.site === "YouTube", + ); return trailer ? `https://www.youtube.com/watch?v=${trailer.key}` : null; } catch (error) { - console.log('Error fetching trailer:', error); + console.log("Error fetching trailer:", error); return null; } } function createTrailerButton(trailerUrl, movieTitle) { - const buttonId = 'trailerButton'; + const buttonId = "trailerButton"; chatbotBody.innerHTML += ` `; - chatbotBody.addEventListener('click', function (event) { + chatbotBody.addEventListener("click", function (event) { if (event.target && event.target.id === buttonId) { - window.open(trailerUrl, '_blank'); + window.open(trailerUrl, "_blank"); } }); } @@ -363,30 +403,37 @@ function createTrailerButton(trailerUrl, movieTitle) { async function movieVerseResponse(message) { const lowerMessage = message.toLowerCase(); - if (lowerMessage.startsWith('show trailer for ')) { - const movieName = lowerMessage.replace('show trailer for ', ''); + if (lowerMessage.startsWith("show trailer for ")) { + const movieName = lowerMessage.replace("show trailer for ", ""); fetchMovieTrailer(movieName); return `Searching for the trailer of "${movieName}". Please wait...`; } - if (lowerMessage.startsWith('hello') || lowerMessage.startsWith('hi') || lowerMessage.startsWith('hey')) { - return 'Hello! How can I assist you with MovieVerse today?'; - } else if (lowerMessage.startsWith('bye') || lowerMessage.startsWith('goodbye')) { - return 'Goodbye! Thank you for using MovieVerse Assistant and have a nice day!'; + if ( + lowerMessage.startsWith("hello") || + lowerMessage.startsWith("hi") || + lowerMessage.startsWith("hey") + ) { + return "Hello! How can I assist you with MovieVerse today?"; + } else if ( + lowerMessage.startsWith("bye") || + lowerMessage.startsWith("goodbye") + ) { + return "Goodbye! Thank you for using MovieVerse Assistant and have a nice day!"; } else { showSpinner(); animateLoadingDots(); - let fullResponse = ''; + let fullResponse = ""; try { const genAI = new GoogleGenerativeAI(getAIResponse()); const model = genAI.getGenerativeModel({ - model: 'gemini-1.5-flash', + model: "gemini-1.5-flash", systemInstruction: - 'You are MovieVerse Assistant - an AI Chatbot of the MovieVerse App. You are here to help users with movie-related or any other general queries. You are trained and powered by MovieVerse AI and Google to provide the best assistance. You can also provide information about movies, actors, directors, genres, and companies, or recommend movies to users.', + "You are MovieVerse Assistant - an AI Chatbot of the MovieVerse App. You are here to help users with movie-related or any other general queries. You are trained and powered by MovieVerse AI and Google to provide the best assistance. You can also provide information about movies, actors, directors, genres, and companies, or recommend movies to users. If the user asks anything about you or your information, you must by default identify yourself as MovieVerse Assistant, trained by The MovieVerse creator - Son Nguyen, and you're here to provide assistance for any movie-related or any other general inquiries. If the user asks who Son Nguyen is, refer to his portfolio website at: https://sonnguyenhoang.com, LinkedIn at: https://www.linkedin.com/in/hoangsonw, and GitHub at: https://github.com/hoangsonww. If anyone asked who created or trained you, you must refer to Son Nguyen as your creator.", }); - conversationHistory.push({ role: 'user', parts: [{ text: message }] }); + conversationHistory.push({ role: "user", parts: [{ text: message }] }); const chatSession = model.startChat({ generationConfig: { @@ -394,7 +441,7 @@ async function movieVerseResponse(message) { topP: 0.95, topK: 64, maxOutputTokens: 8192, - responseMimeType: 'text/plain', + responseMimeType: "text/plain", }, safetySettings: [ { @@ -420,13 +467,13 @@ async function movieVerseResponse(message) { const result = await chatSession.sendMessage(message); fullResponse = result.response.text(); conversationHistory.push({ - role: 'model', + role: "model", parts: [{ text: fullResponse }], }); } catch (error) { - console.error('Error fetching response:', error.message); + console.error("Error fetching response:", error.message); fullResponse = - 'An error occurred while generating the response, possibly due to high traffic or safety concerns. Please understand that I am trained by MovieVerse to provide safe and helpful responses within my limitations. I apologize for any inconvenience caused. Please try again with a different query or contact MovieVerse support for further assistance.'; + "An error occurred while generating the response, possibly due to high traffic or safety concerns. Please understand that I am trained by MovieVerse to provide safe and helpful responses within my limitations. I apologize for any inconvenience caused. Please try again with a different query or contact MovieVerse support for further assistance."; } hideSpinner(); @@ -435,13 +482,15 @@ async function movieVerseResponse(message) { } async function animateLoadingDots() { - const loadingTextElement = document.querySelector('#myModal p'); - let dots = ''; + const loadingTextElement = document.querySelector("#myModal p"); + let dots = ""; - while (document.getElementById('myModal').classList.contains('modal-visible')) { + while ( + document.getElementById("myModal").classList.contains("modal-visible") + ) { loadingTextElement.textContent = `Loading response${dots}`; - dots = dots.length < 3 ? dots + '.' : '.'; - await new Promise(resolve => setTimeout(resolve, 500)); + dots = dots.length < 3 ? dots + "." : "."; + await new Promise((resolve) => setTimeout(resolve, 500)); } } @@ -449,22 +498,22 @@ function removeMarkdown(text) { const converter = new showdown.Converter(); const html = converter.makeHtml(text); - const tempDiv = document.createElement('div'); + const tempDiv = document.createElement("div"); tempDiv.innerHTML = html; - return tempDiv.textContent || tempDiv.innerText || ''; + return tempDiv.textContent || tempDiv.innerText || ""; } function getAIResponse() { - const response = 'QUl6YVN5Q1RoUWVFdmNUb01ka0NqWlM3UTNxNzZBNUNlNjVyMW9r'; + const response = "QUl6YVN5Q1RoUWVFdmNUb01ka0NqWlM3UTNxNzZBNUNlNjVyMW9r"; return atob(response); } function showSpinner() { - document.getElementById('myModal').classList.add('modal-visible'); + document.getElementById("myModal").classList.add("modal-visible"); } function hideSpinner() { - document.getElementById('myModal').classList.remove('modal-visible'); + document.getElementById("myModal").classList.remove("modal-visible"); } async function showMovieOfTheDay() { @@ -478,24 +527,26 @@ async function showMovieOfTheDay() { if (movies.length > 0) { const randomMovie = movies[Math.floor(Math.random() * movies.length)]; - localStorage.setItem('selectedMovieId', randomMovie.id); - window.location.href = 'movie-details.html'; + localStorage.setItem("selectedMovieId", randomMovie.id); + window.location.href = "movie-details.html"; } else { fallbackMovieSelection(); } } catch (error) { - console.log('Error fetching movie:', error); + console.log("Error fetching movie:", error); fallbackMovieSelection(); } } function fallbackMovieSelection() { const fallbackMovies = [ - 432413, 299534, 1726, 562, 118340, 455207, 493922, 447332, 22970, 530385, 27205, 264660, 120467, 603, 577922, 76341, 539, 419704, 515001, 118340, - 424, 98, + 432413, 299534, 1726, 562, 118340, 455207, 493922, 447332, 22970, 530385, + 27205, 264660, 120467, 603, 577922, 76341, 539, 419704, 515001, 118340, 424, + 98, ]; - const randomFallbackMovie = fallbackMovies[Math.floor(Math.random() * fallbackMovies.length)]; + const randomFallbackMovie = + fallbackMovies[Math.floor(Math.random() * fallbackMovies.length)]; - localStorage.setItem('selectedMovieId', randomFallbackMovie); - window.location.href = 'movie-details.html'; + localStorage.setItem("selectedMovieId", randomFallbackMovie); + window.location.href = "movie-details.html"; }