diff --git a/MovieVerse-Mobile/app/js/LICENSE b/MovieVerse-Mobile/app/js/LICENSE index 19d3d823..a0ef1b39 100644 --- a/MovieVerse-Mobile/app/js/LICENSE +++ b/MovieVerse-Mobile/app/js/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2023 Son Nguyen Hoang +Copyright (c) 2024 Son Nguyen Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/MovieVerse-Mobile/app/js/about.js b/MovieVerse-Mobile/app/js/about.js index 9e0b86c1..f26e588f 100644 --- a/MovieVerse-Mobile/app/js/about.js +++ b/MovieVerse-Mobile/app/js/about.js @@ -17,7 +17,7 @@ async function showMovieOfTheDay() { } } catch (error) { - console.error('Error fetching movie:', error); + console.log('Error fetching movie:', error); fallbackMovieSelection(); } } @@ -43,6 +43,7 @@ function getMovieVerseData(input) { 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]; const randomFallbackMovie = fallbackMovies[Math.floor(Math.random() * fallbackMovies.length)]; + localStorage.setItem('selectedMovieId', randomFallbackMovie); window.location.href = 'movie-details.html'; } @@ -131,7 +132,7 @@ async function fetchGenreMap() { localStorage.setItem('genreMap', JSON.stringify(genreMap)); } catch (error) { - console.error('Error fetching genre map:', error); + console.log('Error fetching genre map:', error); } } @@ -163,7 +164,7 @@ async function rotateUserStats() { { label: "Favorite Movies", getValue: () => { - const favoritedMovies = JSON.parse(localStorage.getItem('favoritesMovies')) || []; + const favoritedMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; return favoritedMovies.length; } }, @@ -171,8 +172,37 @@ async function rotateUserStats() { label: "Favorite Genre", getValue: () => { const mostCommonGenreCode = getMostCommonGenre(); - const genreMap = JSON.parse(localStorage.getItem('genreMap')) || {}; - return genreMap[mostCommonGenreCode] || 'Not Available'; + const genreMapString = localStorage.getItem('genreMap'); + if (!genreMapString) { + 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'; + } + + let genreObject; + if (Array.isArray(genreMap)) { + genreObject = genreMap.reduce((acc, genre) => { + acc[genre.id] = genre.name; + return acc; + }, {}); + } + 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'; + } + + return genreObject[mostCommonGenreCode] || 'Not Available'; } }, { label: "Watchlists Created", getValue: () => localStorage.getItem('watchlistsCreated') || 0 }, diff --git a/MovieVerse-Mobile/app/js/actor-details.js b/MovieVerse-Mobile/app/js/actor-details.js index 38c00f99..1b7c45c0 100644 --- a/MovieVerse-Mobile/app/js/actor-details.js +++ b/MovieVerse-Mobile/app/js/actor-details.js @@ -126,7 +126,7 @@ async function fetchActorDetails(actorId) { hideSpinner(); } catch (error) { - console.error('Error fetching actor details:', error); + console.log('Error fetching actor details:', error); document.getElementById('actor-details-container').innerHTML = `

Actor details not found - try again with a different actor.

@@ -135,7 +135,7 @@ async function fetchActorDetails(actorId) { } } -function populateActorDetails(actor, credits) { +async function populateActorDetails(actor, credits) { const actorImage = document.getElementById('actor-image'); const actorName = document.getElementById('actor-name'); const actorDescription = document.getElementById('actor-description'); @@ -169,17 +169,18 @@ function populateActorDetails(actor, credits) { } actorDescription.innerHTML = ` -

Biography: ${actor.biography || 'N/A'}

-

Date of Birth: ${actor.birthday || 'N/A'}

-

Date of Death: ${actor.deathday || 'N/A'}

+

Biography: ${actor.biography || 'Information Unavailable'}

+

Also Known As: ${actor.also_known_as.join(', ') || 'Information Unavailable'}

+

Date of Birth: ${actor.birthday || 'Information Unavailable'}

+

Date of Death: ${actor.deathday || 'Information Unavailable'}

Age: ${ageOrStatus}

-

Place of Birth: ${actor.place_of_birth || 'N/A'}

-

Known For: ${actor.known_for_department || 'N/A'}

-

Height: ${actor.height || 'N/A'}

+

Place of Birth: ${actor.place_of_birth || 'Information Unavailable'}

+

Known For: ${actor.known_for_department || 'Information Unavailable'}

+

Height: ${actor.height || 'Information Unavailable'}

`; const gender = document.createElement('div'); - gender.innerHTML = `

Gender: ${actor.gender === 1 ? 'Female' : actor.gender === 2 ? 'Male' : 'N/A'}

`; + gender.innerHTML = `

Gender: ${actor.gender === 1 ? 'Female' : actor.gender === 2 ? 'Male' : 'Information Unavailable'}

`; actorDescription.appendChild(gender); const popularity = document.createElement('div'); @@ -192,7 +193,8 @@ function populateActorDetails(actor, credits) { const movieList = document.createElement('div'); movieList.classList.add('movie-list'); - credits.cast.forEach(movie => { + + credits.cast.forEach((movie, index) => { const movieLink = document.createElement('span'); movieLink.textContent = movie.title; movieLink.classList.add('movie-link'); @@ -201,10 +203,157 @@ function populateActorDetails(actor, credits) { window.location.href = 'movie-details.html'; }); movieList.appendChild(movieLink); - movieList.appendChild(document.createTextNode(', ')); + + if (index < credits.cast.length - 1) { + movieList.appendChild(document.createTextNode(', ')); + } }); filmographyHeading.appendChild(movieList); + + const mediaUrl = `https://${getMovieVerseData()}/3/person/${actor.id}/images?${generateMovieNames()}${getMovieCode()}`; + const mediaResponse = await fetch(mediaUrl); + const mediaData = await mediaResponse.json(); + const images = mediaData.profiles; + + const detailsContainer = document.getElementById('actor-description'); + + let mediaContainer = document.getElementById('media-container'); + if (!mediaContainer) { + mediaContainer = document.createElement('div'); + mediaContainer.id = 'media-container'; + mediaContainer.style = ` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + width: 450px; + margin: 20px auto; + overflow: hidden; + max-width: 100%; + box-sizing: border-box; + `; + detailsContainer.appendChild(mediaContainer); + } + + let mediaTitle = document.getElementById('media-title'); + if (!mediaTitle) { + mediaTitle = document.createElement('p'); + mediaTitle.id = 'media-title'; + mediaTitle.textContent = 'Media:'; + mediaTitle.style = ` + font-weight: bold; + align-self: center; + margin-bottom: 5px; + `; + } + + detailsContainer.appendChild(mediaTitle); + detailsContainer.appendChild(mediaContainer); + + let imageElement = document.getElementById('series-media-image'); + if (!imageElement) { + imageElement = document.createElement('img'); + imageElement.id = 'series-media-image'; + imageElement.style = ` + max-width: 100%; + max-height: 210px; + transition: opacity 0.5s ease-in-out; + opacity: 1; + border-radius: 16px; + cursor: pointer; + `; + mediaContainer.appendChild(imageElement); + } + + if (images.length > 0) { + imageElement.src = `https://image.tmdb.org/t/p/w1280${images[0].file_path}`; + } + + imageElement.addEventListener('click', function() { + const imageUrl = this.src; + const modalHtml = ` +
+ + × +
+ `; + document.body.insertAdjacentHTML('beforeend', modalHtml); + const modal = document.getElementById('image-modal'); + const closeModalBtn = document.getElementById('removeBtn'); + + closeModalBtn.onclick = function() { + modal.remove(); + }; + + modal.addEventListener('click', function(event) { + if (event.target === this) { + this.remove(); + } + }); + }); + + let prevButton = document.getElementById('prev-media-button'); + let nextButton = document.getElementById('next-media-button'); + if (!prevButton || !nextButton) { + prevButton = document.createElement('button'); + nextButton = document.createElement('button'); + prevButton.id = 'prev-media-button'; + nextButton.id = 'next-media-button'; + prevButton.innerHTML = ''; + nextButton.innerHTML = ''; + + [prevButton, nextButton].forEach(button => { + button.style = ` + position: absolute; + top: 50%; + transform: translateY(-50%); + background-color: #7378c5; + color: white; + border-radius: 8px; + height: 30px; + width: 30px; + border: none; + cursor: pointer; + `; + button.onmouseover = () => button.style.backgroundColor = '#ff8623'; + button.onmouseout = () => button.style.backgroundColor = '#7378c5'; + }); + + prevButton.style.left = '0'; + nextButton.style.right = '0'; + + mediaContainer.appendChild(prevButton); + mediaContainer.appendChild(nextButton); + } + + let currentIndex = 0; + prevButton.onclick = () => navigateMedia(images, imageElement, -1); + nextButton.onclick = () => navigateMedia(images, imageElement, 1); + + function navigateMedia(images, imgElement, direction) { + currentIndex += direction; + if (currentIndex < 0) { + currentIndex = images.length - 1; + } else if (currentIndex >= images.length) { + currentIndex = 0; + } + imgElement.style.opacity = '0'; + setTimeout(() => { + imgElement.src = `https://image.tmdb.org/t/p/w1280${images[currentIndex].file_path}`; + imgElement.style.opacity = '1'; + }, 420); + } + + if (window.innerWidth <= 767) { + mediaContainer.style.width = 'calc(100% - 40px)'; + } + + if (images.length === 0) { + mediaContainer.innerHTML = '

No media available

'; + } + applySettings(); } @@ -234,7 +383,7 @@ async function fetchGenreMap() { localStorage.setItem('genreMap', JSON.stringify(genreMap)); } catch (error) { - console.error('Error fetching genre map:', error); + console.log('Error fetching genre map:', error); } } @@ -266,7 +415,7 @@ async function rotateUserStats() { { label: "Favorite Movies", getValue: () => { - const favoritedMovies = JSON.parse(localStorage.getItem('favoritesMovies')) || []; + const favoritedMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; return favoritedMovies.length; } }, @@ -274,8 +423,37 @@ async function rotateUserStats() { label: "Favorite Genre", getValue: () => { const mostCommonGenreCode = getMostCommonGenre(); - const genreMap = JSON.parse(localStorage.getItem('genreMap')) || {}; - return genreMap[mostCommonGenreCode] || 'Not Available'; + const genreMapString = localStorage.getItem('genreMap'); + if (!genreMapString) { + 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'; + } + + let genreObject; + if (Array.isArray(genreMap)) { + genreObject = genreMap.reduce((acc, genre) => { + acc[genre.id] = genre.name; + return acc; + }, {}); + } + 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'; + } + + return genreObject[mostCommonGenreCode] || 'Not Available'; } }, { label: "Watchlists Created", getValue: () => localStorage.getItem('watchlistsCreated') || 0 }, @@ -463,7 +641,7 @@ async function showMovieOfTheDay() { } } catch (error) { - console.error('Error fetching movie:', error); + console.log('Error fetching movie:', error); fallbackMovieSelection(); } } diff --git a/MovieVerse-Mobile/app/js/add-to-favorites.js b/MovieVerse-Mobile/app/js/add-to-favorites.js index bfbb04e3..0df2c998 100644 --- a/MovieVerse-Mobile/app/js/add-to-favorites.js +++ b/MovieVerse-Mobile/app/js/add-to-favorites.js @@ -53,29 +53,40 @@ export async function checkAndUpdateFavoriteButton() { const movieId = localStorage.getItem('selectedMovieId'); if (!movieId) { - console.error('Movie ID is missing'); + console.log('Movie ID is missing'); return; } - if (!userEmail) { - console.log('User is not signed in. Checking local storage for favorites.'); - const localFavorites = JSON.parse(localStorage.getItem('favoritesMovies')) || []; - updateFavoriteButton(movieId, localFavorites); - return; - } + try { + if (!userEmail) { + console.log('User is not signed in. Checking local storage for favorites.'); + const localFavorites = JSON.parse(localStorage.getItem('moviesFavorited')) || []; + updateFavoriteButton(movieId, localFavorites); + return; + } - const usersRef = query(collection(db, "MovieVerseUsers"), where("email", "==", userEmail)); - const querySnapshot = await getDocs(usersRef); + const usersRef = query(collection(db, "MovieVerseUsers"), where("email", "==", userEmail)); + const querySnapshot = await getDocs(usersRef); - if (querySnapshot.empty) { - console.error('No user found with that email'); - return; - } + if (querySnapshot.empty) { + console.log('No user found with that email'); + return; + } - const userData = querySnapshot.docs[0].data(); - const favorites = userData.favoritesMovies || []; + const userData = querySnapshot.docs[0].data(); + const favorites = userData.favoritesMovies || []; - updateFavoriteButton(movieId, favorites); + updateFavoriteButton(movieId, favorites); + } + catch (error) { + if (error.code === 'resource-exhausted') { + console.log('Firebase quota exceeded. Checking local storage for favorites.'); + const localFavorites = JSON.parse(localStorage.getItem('moviesFavorited')) || []; + updateFavoriteButton(movieId, localFavorites); + } else { + console.error('An error occurred:', error); + } + } } function updateFavoriteButton(movieId, favorites) { @@ -88,7 +99,7 @@ function updateFavoriteButton(movieId, favorites) { } else { favoriteButton.classList.remove('favorited'); - favoriteButton.style.background = 'transparent'; + favoriteButton.style.backgroundColor = 'rgba(255, 255, 255, 0.05)'; favoriteButton.title = 'Add to favorites'; } } @@ -109,75 +120,125 @@ export async function toggleFavorite() { const movieId = localStorage.getItem('selectedMovieId'); if (!movieId) { - console.error('Movie ID is missing'); + console.log('Movie ID is missing'); return; } const movieGenre = await getMovieGenre(movieId); - if (!userEmail) { - console.log('User is not signed in. Using localStorage for favorites.'); - let favoritesMovies = JSON.parse(localStorage.getItem('favoritesMovies')) || []; - let favoriteGenres = JSON.parse(localStorage.getItem('favoriteGenres')) || []; + try { + if (!userEmail) { + console.log('User is not signed in. Using localStorage for favorites.'); + let favoritesMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; + let favoriteGenres = JSON.parse(localStorage.getItem('favoriteGenres')) || []; + + if (favoritesMovies.includes(movieId)) { + favoritesMovies = favoritesMovies.filter(id => id !== movieId); + favoriteGenres = favoriteGenres.filter(genre => favoritesMovies.some(id => { + const movieDetails = JSON.parse(localStorage.getItem(id)); + return movieDetails && movieDetails.genre === genre; + })); + } + else { + favoritesMovies.push(movieId); + if (!favoriteGenres.includes(movieGenre)) { + favoriteGenres.push(movieGenre); + } + } + + localStorage.setItem('moviesFavorited', JSON.stringify(favoritesMovies)); + localStorage.setItem('favoriteGenres', JSON.stringify(favoriteGenres)); + + console.log('Favorites movies updated successfully in localStorage'); + window.location.reload(); + return; + } + + const usersRef = query(collection(db, "MovieVerseUsers"), where("email", "==", userEmail)); + const querySnapshot = await getDocs(usersRef); - if (favoritesMovies.includes(movieId)) { - favoritesMovies = favoritesMovies.filter(id => id !== movieId); - favoriteGenres = favoriteGenres.filter(genre => genre !== movieGenre || favoritesMovies.some(id => localStorage.getItem(id).genre === movieGenre)); + let userDocRef; + + if (querySnapshot.empty && userEmail === "") { + const newUserRef = doc(collection(db, "MovieVerseUsers")); + userDocRef = newUserRef; + await setDoc(newUserRef, {email: userEmail, favoritesMovies: [movieId]}); + console.log('New user created with favorite movie.'); + } + else if (!querySnapshot.empty) { + userDocRef = doc(db, "MovieVerseUsers", querySnapshot.docs[0].id); } else { - favoritesMovies.push(movieId); - if (!favoriteGenres.includes(movieGenre)) { - favoriteGenres.push(movieGenre); - } + console.log('No user found with that email and user is supposed to be signed in.'); + return; } - localStorage.setItem('favoritesMovies', JSON.stringify(favoritesMovies)); - localStorage.setItem('favoriteGenres', JSON.stringify(favoriteGenres)); + if (userDocRef) { + const userData = querySnapshot.empty ? {favoritesMovies: []} : querySnapshot.docs[0].data(); + let favoritesMovies = userData.favoritesMovies || []; + let favoriteGenres = JSON.parse(localStorage.getItem('favoriteGenres')) || []; + + if (favoritesMovies.includes(movieId)) { + favoritesMovies = favoritesMovies.filter(id => id !== movieId); + favoriteGenres = favoriteGenres.filter(genre => favoritesMovies.some(id => { + const movieDetails = JSON.parse(localStorage.getItem(id)); + return movieDetails && movieDetails.genre === genre; + })); + } + else { + favoritesMovies.push(movieId); + if (!favoriteGenres.includes(movieGenre)) { + favoriteGenres.push(movieGenre); + } + } - console.log('Favorites movies updated successfully in localStorage'); - window.location.reload(); - return; - } + await updateDoc(userDocRef, {favoritesMovies}); + localStorage.setItem('favoriteGenres', JSON.stringify(favoriteGenres)); + console.log('Favorites movies updated successfully in Firestore'); + } - const usersRef = query(collection(db, "MovieVerseUsers"), where("email", "==", userEmail)); - const querySnapshot = await getDocs(usersRef); + updateMoviesFavorited(movieId); - let userDocRef; + } catch (error) { + if (error.code === 'resource-exhausted') { + console.log('Firebase quota exceeded. Using localStorage for favorites.'); + let favoritesMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; + let favoriteGenres = JSON.parse(localStorage.getItem('favoriteGenres')) || []; - if (querySnapshot.empty && userEmail === "") { - const newUserRef = doc(collection(db, "MovieVerseUsers")); - userDocRef = newUserRef; - await setDoc(newUserRef, { email: userEmail, favoritesMovies: [movieId] }); - console.log('New user created with favorite movie.'); - } - else if (!querySnapshot.empty) { - userDocRef = doc(db, "MovieVerseUsers", querySnapshot.docs[0].id); - } - else { - console.error('No user found with that email and user is supposed to be signed in.'); - return; - } - - if (userDocRef) { - const userData = querySnapshot.empty ? { favoritesMovies: [] } : querySnapshot.docs[0].data(); - let favoritesMovies = userData.favoritesMovies || []; - let favoriteGenres = JSON.parse(localStorage.getItem('favoriteGenres')) || []; + if (favoritesMovies.includes(movieId)) { + favoritesMovies = favoritesMovies.filter(id => id !== movieId); + favoriteGenres = favoriteGenres.filter(genre => favoritesMovies.some(id => { + const movieDetails = JSON.parse(localStorage.getItem(id)); + return movieDetails && movieDetails.genre === genre; + })); + } + else { + favoritesMovies.push(movieId); + if (!favoriteGenres.includes(movieGenre)) { + favoriteGenres.push(movieGenre); + } + } - if (favoritesMovies.includes(movieId)) { - favoritesMovies = favoritesMovies.filter(id => id !== movieId); - favoriteGenres = favoriteGenres.filter(genre => genre !== movieGenre || favoritesMovies.some(id => localStorage.getItem(id).genre === movieGenre)); + localStorage.setItem('moviesFavorited', JSON.stringify(favoritesMovies)); + localStorage.setItem('favoriteGenres', JSON.stringify(favoriteGenres)); + console.log('Favorites movies updated successfully in localStorage'); + window.location.reload(); + return; } else { - favoritesMovies.push(movieId); - if (!favoriteGenres.includes(movieGenre)) { - favoriteGenres.push(movieGenre); - } + console.error('An error occurred:', error); } - await updateDoc(userDocRef, { favoritesMovies }); - localStorage.setItem('favoriteGenres', JSON.stringify(favoriteGenres)); - console.log('Favorites movies updated successfully in Firestore'); + updateMoviesFavorited(movieId); } window.location.reload(); } + +function updateMoviesFavorited(movieId) { + let favoritedMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; + if (!favoritedMovies.includes(movieId)) { + favoritedMovies.push(movieId); + localStorage.setItem('moviesFavorited', JSON.stringify(favoritedMovies)); + } +} \ No newline at end of file diff --git a/MovieVerse-Mobile/app/js/add-to-tv-favorites.js b/MovieVerse-Mobile/app/js/add-to-tv-favorites.js index f400c046..da298d83 100644 --- a/MovieVerse-Mobile/app/js/add-to-tv-favorites.js +++ b/MovieVerse-Mobile/app/js/add-to-tv-favorites.js @@ -43,79 +43,113 @@ const app = initializeApp(firebaseConfig); const db = getFirestore(app); export async function toggleFavoriteTVSeries() { - let userEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); const tvSeriesId = localStorage.getItem('selectedTvSeriesId'); - if (!tvSeriesId) { - console.error('TV Series ID is missing'); - return; - } + try { + let userEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); + + if (!tvSeriesId) { + console.log('TV Series ID is missing'); + return; + } + + if (!userEmail) { + console.log('User is not signed in. Using localStorage for favorites.'); + let favoritesTVSeries = JSON.parse(localStorage.getItem('favoritesTVSeries')) || []; + if (favoritesTVSeries.includes(tvSeriesId)) { + favoritesTVSeries = favoritesTVSeries.filter(id => id !== tvSeriesId); + } else { + favoritesTVSeries.push(tvSeriesId); + } + localStorage.setItem('favoritesTVSeries', JSON.stringify(favoritesTVSeries)); + console.log('Favorites TV Series updated successfully in localStorage'); + await checkAndUpdateFavoriteButtonTVSeries(); + return; + } + + const usersRef = query(collection(db, "MovieVerseUsers"), where("email", "==", userEmail)); + const querySnapshot = await getDocs(usersRef); + + let userDocRef; + if (querySnapshot.empty) { + console.log('Signed-in user does not have a Firestore document.'); + return; + } else { + userDocRef = doc(db, "MovieVerseUsers", querySnapshot.docs[0].id); + } + + const userData = querySnapshot.docs[0].data(); + let favoritesTVSeries = userData.favoritesTVSeries || []; - if (!userEmail) { - console.log('User is not signed in. Using localStorage for favorites.'); - let favoritesTVSeries = JSON.parse(localStorage.getItem('favoritesTVSeries')) || []; if (favoritesTVSeries.includes(tvSeriesId)) { favoritesTVSeries = favoritesTVSeries.filter(id => id !== tvSeriesId); } else { favoritesTVSeries.push(tvSeriesId); } - localStorage.setItem('favoritesTVSeries', JSON.stringify(favoritesTVSeries)); - console.log('Favorites TV Series updated successfully in localStorage'); - await checkAndUpdateFavoriteButtonTVSeries(); - return; - } - const usersRef = query(collection(db, "MovieVerseUsers"), where("email", "==", userEmail)); - const querySnapshot = await getDocs(usersRef); + await updateDoc(userDocRef, {favoritesTVSeries}); + console.log('Favorites TV Series updated successfully in Firestore'); + await checkAndUpdateFavoriteButtonTVSeries(); + window.location.reload(); - let userDocRef; - if (querySnapshot.empty) { - console.error('Signed-in user does not have a Firestore document.'); - return; - } - else { - userDocRef = doc(db, "MovieVerseUsers", querySnapshot.docs[0].id); } - - const userData = querySnapshot.docs[0].data(); - let favoritesTVSeries = userData.favoritesTVSeries || []; - - if (favoritesTVSeries.includes(tvSeriesId)) { - favoritesTVSeries = favoritesTVSeries.filter(id => id !== tvSeriesId); - } else { - favoritesTVSeries.push(tvSeriesId); + catch (error) { + if (error.code === 'resource-exhausted') { + console.log('Firebase quota exceeded. Using localStorage for favorites.'); + let favoritesTVSeries = JSON.parse(localStorage.getItem('favoritesTVSeries')) || []; + if (favoritesTVSeries.includes(tvSeriesId)) { + favoritesTVSeries = favoritesTVSeries.filter(id => id !== tvSeriesId); + } + else { + favoritesTVSeries.push(tvSeriesId); + } + localStorage.setItem('favoritesTVSeries', JSON.stringify(favoritesTVSeries)); + console.log('Favorites TV Series updated successfully in localStorage'); + } + else { + console.error('An error occurred:', error); + } + window.location.reload(); } - - await updateDoc(userDocRef, { favoritesTVSeries }); - console.log('Favorites TV Series updated successfully in Firestore'); - await checkAndUpdateFavoriteButtonTVSeries(); } export async function checkAndUpdateFavoriteButtonTVSeries() { let userEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); const tvSeriesId = localStorage.getItem('selectedTvSeriesId'); - if (!tvSeriesId) { - console.error('TV Series ID is missing'); - return; - } + try { - let favoritesTVSeries = []; + if (!tvSeriesId) { + console.log('TV Series ID is missing'); + return; + } - if (userEmail) { - const usersRef = query(collection(db, "MovieVerseUsers"), where("email", "==", userEmail)); - const querySnapshot = await getDocs(usersRef); + let favoritesTVSeries = []; + + if (userEmail) { + const usersRef = query(collection(db, "MovieVerseUsers"), where("email", "==", userEmail)); + const querySnapshot = await getDocs(usersRef); - if (!querySnapshot.empty) { - const userData = querySnapshot.docs[0].data(); - favoritesTVSeries = userData.favoritesTVSeries || []; + if (!querySnapshot.empty) { + const userData = querySnapshot.docs[0].data(); + favoritesTVSeries = userData.favoritesTVSeries || []; + } + } else { + favoritesTVSeries = JSON.parse(localStorage.getItem('favoritesTVSeries')) || []; } + + updateFavoriteButtonTVSeries(tvSeriesId, favoritesTVSeries); } - else { - favoritesTVSeries = JSON.parse(localStorage.getItem('favoritesTVSeries')) || []; + catch (error) { + if (error.code === 'resource-exhausted') { + console.log('Firebase quota exceeded. Using localStorage for favorites.'); + let favoritesTVSeries = JSON.parse(localStorage.getItem('favoritesTVSeries')) || []; + updateFavoriteButtonTVSeries(tvSeriesId, favoritesTVSeries); + } + else { + console.error('An error occurred:', error); + } } - - updateFavoriteButtonTVSeries(tvSeriesId, favoritesTVSeries); } function updateFavoriteButtonTVSeries(tvSeriesId, favoritesTVSeries) { @@ -129,6 +163,6 @@ function updateFavoriteButtonTVSeries(tvSeriesId, favoritesTVSeries) { else { favoriteBtn.classList.remove('favorited'); favoriteBtn.title = 'Add to Favorites'; - favoriteBtn.style.backgroundColor = 'transparent'; + favoriteBtn.style.backgroundColor = 'rgba(255, 255, 255, 0.05)'; } } \ No newline at end of file diff --git a/MovieVerse-Mobile/app/js/analytics.js b/MovieVerse-Mobile/app/js/analytics.js index ae2d7801..82b83a28 100644 --- a/MovieVerse-Mobile/app/js/analytics.js +++ b/MovieVerse-Mobile/app/js/analytics.js @@ -32,7 +32,7 @@ async function fetchData(url) { return await response.json(); } catch (error) { - console.error('Error fetching data:', error); + console.log('Error fetching data:', error); return null; } } @@ -334,6 +334,82 @@ async function loadMoviesByProductionCountriesChart() { hideSpinner(); } +async function loadTopRatedMoviesPerYearChart() { + const years = []; + const topMovies = []; + const currentYear = new Date().getFullYear(); + + for (let year = currentYear - 10; year <= currentYear; year++) { + years.push(year); + const response = await fetchData(`${BASE_URL}/discover/movie?${generateMovieNames()}=${string}&primary_release_year=${year}&sort_by=vote_average.desc&vote_count.gte=100`); + if (response.results.length > 0) { + topMovies.push(response.results[0].vote_average); + } + else { + topMovies.push(0); + } + } + + createChart('chart11', 'bar', { + labels: years, + datasets: [{ + label: 'Top Rated Movie Score', + data: topMovies, + backgroundColor: 'rgba(255, 159, 64, 1)', + borderColor: 'rgba(255, 159, 64, 1)', + borderWidth: 1 + }] + }); +} + +async function loadTotalMovieVotesOverYearsChart() { + const years = []; + const totalVoteCounts = []; + const currentYear = new Date().getFullYear(); + + for (let year = currentYear - 10; year <= currentYear; year++) { + years.push(year); + const response = await fetchData(`${BASE_URL}/discover/movie?${generateMovieNames()}=${string}&primary_release_year=${year}`); + const yearlyTotalVotes = response.results.reduce((sum, movie) => sum + movie.vote_count, 0); + totalVoteCounts.push(yearlyTotalVotes); + } + + createChart('chartVotesOverYears', 'line', { + labels: years, + datasets: [{ + label: 'Total Movie Votes', + data: totalVoteCounts, + backgroundColor: 'rgba(255, 193, 7, 1)', + borderColor: 'rgba(255, 193, 7, 1)', + borderWidth: 1 + }] + }); +} + +async function loadHighlyRatedMoviesOverYearsChart() { + const years = []; + const highRatedMovieCounts = []; + const currentYear = new Date().getFullYear(); + const startYear = currentYear - 10; + + for (let year = startYear; year <= currentYear; year++) { + years.push(year); + const response = await fetchData(`${BASE_URL}/discover/movie?${generateMovieNames()}=${string}&primary_release_year=${year}&vote_average.gte=8`); + highRatedMovieCounts.push(response.total_results); + } + + createChart('chartHighlyRatedMovies', 'line', { + labels: years, + datasets: [{ + label: 'Highly Rated Movies (Rating >= 8)', + data: highRatedMovieCounts, + backgroundColor: 'rgba(0, 206, 209, 1)', + borderColor: 'rgba(0, 206, 209, 1)', + borderWidth: 1 + }] + }); +} + function loadAllCharts() { loadMoviesByYearChart(); loadGenrePopularityChart(); @@ -344,6 +420,9 @@ function loadAllCharts() { loadMovieReleaseDatesByMonthChart(); loadMoviesByDecadeChart(); loadMoviesByProductionCountriesChart(); + loadTopRatedMoviesPerYearChart(); + loadTotalMovieVotesOverYearsChart(); + loadHighlyRatedMoviesOverYearsChart(); } document.addEventListener('DOMContentLoaded', loadAllCharts); @@ -369,7 +448,7 @@ async function showMovieOfTheDay() { } } catch (error) { - console.error('Error fetching movie:', error); + console.log('Error fetching movie:', error); fallbackMovieSelection(); } } @@ -465,7 +544,7 @@ async function fetchGenreMap() { localStorage.setItem('genreMap', JSON.stringify(genreMap)); } catch (error) { - console.error('Error fetching genre map:', error); + console.log('Error fetching genre map:', error); } } @@ -497,7 +576,7 @@ async function rotateUserStats() { { label: "Favorite Movies", getValue: () => { - const favoritedMovies = JSON.parse(localStorage.getItem('favoritesMovies')) || []; + const favoritedMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; return favoritedMovies.length; } }, @@ -505,8 +584,37 @@ async function rotateUserStats() { label: "Favorite Genre", getValue: () => { const mostCommonGenreCode = getMostCommonGenre(); - const genreMap = JSON.parse(localStorage.getItem('genreMap')) || {}; - return genreMap[mostCommonGenreCode] || 'Not Available'; + const genreMapString = localStorage.getItem('genreMap'); + if (!genreMapString) { + 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'; + } + + let genreObject; + if (Array.isArray(genreMap)) { + genreObject = genreMap.reduce((acc, genre) => { + acc[genre.id] = genre.name; + return acc; + }, {}); + } + 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'; + } + + return genreObject[mostCommonGenreCode] || 'Not Available'; } }, { label: "Watchlists Created", getValue: () => localStorage.getItem('watchlistsCreated') || 0 }, @@ -647,4 +755,4 @@ function handleSearch() { const searchQuery = document.getElementById('search').value; localStorage.setItem('searchQuery', searchQuery); window.location.href = 'search.html'; -} \ No newline at end of file +} diff --git a/MovieVerse-Mobile/app/js/chat-auxiliary.js b/MovieVerse-Mobile/app/js/chat-auxiliary.js new file mode 100644 index 00000000..e0096978 --- /dev/null +++ b/MovieVerse-Mobile/app/js/chat-auxiliary.js @@ -0,0 +1,288 @@ +document.addEventListener('DOMContentLoaded', function() { + const searchBar = document.getElementById('search'); + const searchButton = document.getElementById('button-search'); + const myHeading = document.getElementById('my-heading'); + const localTime = document.getElementById('local-time'); + + function toggleVisibility() { + const query = searchBar.value.trim(); + if (query) { + if (window.innerWidth > 800) { + myHeading.style.visibility = 'hidden'; + myHeading.style.opacity = '0'; + localTime.style.visibility = 'hidden'; + localTime.style.opacity = '0'; + } + } + else { + myHeading.style.visibility = 'visible'; + myHeading.style.opacity = '1'; + localTime.style.visibility = 'visible'; + localTime.style.opacity = '1'; + } +} + searchBar.addEventListener('input', toggleVisibility); + toggleVisibility(); +}); + +document.addEventListener('DOMContentLoaded', function() { + const searchInput = document.getElementById('search'); + const viewAllResultsBtn = document.getElementById('view-all-results'); + const clearSearchBtn = document.getElementById('clear-search'); + const searchResultsContainer = document.getElementById('search-results'); + const myHeading = document.getElementById('my-heading'); + const localTime = document.getElementById('local-time'); + const searchButton = document.getElementById('button-search'); + + function toggleButtons() { + const query = searchInput.value.trim(); + viewAllResultsBtn.style.display = query ? 'inline-block' : 'none'; + clearSearchBtn.style.display = query ? 'inline-block' : 'none'; + } + + clearSearchBtn.addEventListener('click', function() { + searchInput.value = ''; + searchResultsContainer.innerHTML = ''; + toggleButtons(); + searchInput.focus(); + + myHeading.style.visibility = 'visible'; + myHeading.style.opacity = '1'; + localTime.style.visibility = 'visible'; + localTime.style.opacity = '1'; + }); + + toggleButtons(); + searchInput.addEventListener('input', toggleButtons); +}); + +function showSpinner() { + document.getElementById('myModal').classList.add('modal-visible'); +} + +function hideSpinner() { + document.getElementById('myModal').classList.remove('modal-visible'); +} + +document.addEventListener('DOMContentLoaded', function() { + document.getElementById('search').addEventListener('input', function(e) { + showSpinner(); + const viewAllResultsBtn = document.getElementById('view-all-results'); + const searchInput = document.getElementById('search'); + const query = e.target.value.trim(); + const searchResultsContainer = document.getElementById('search-results'); + + viewAllResultsBtn.style.display = query ? 'block' : 'none'; + + function toggleButtons() { + viewAllResultsBtn.style.display = query ? 'inline-block' : 'none'; + const clearSearchBtn = document.getElementById('clear-search'); + clearSearchBtn.style.display = query ? 'inline-block' : 'none'; + } + + if (query) { + const searchURL = `https://${getMovieVerseData()}/3/search/multi?${generateMovieNames()}${getMovieCode()}&query=${encodeURIComponent(query)}`; + fetch(searchURL) + .then(response => response.json()) + .then(data => { + const sortedResults = data.results.sort((a, b) => b.popularity - a.popularity); + displaySearchResults(sortedResults.slice(0, 5)); + }) + .catch(err => console.log("Fetching error:", err)); + } + else { + searchInput.value = ''; + searchResultsContainer.innerHTML = ''; + toggleButtons(); + searchInput.focus(); + } + + searchInput.addEventListener('input', function() { + if (searchInput.value.trim()) { + viewAllResultsBtn.style.display = 'block'; + } + else { + viewAllResultsBtn.style.display = 'none'; + } + }); + + viewAllResultsBtn.addEventListener('click', function() { + const searchQuery = searchInput.value.trim(); + if (searchQuery) { + localStorage.setItem('searchQuery', searchQuery); + window.location.href = 'search.html'; + } + else { + alert('Please enter a search query.'); + } + }); + + hideSpinner(); + }); + + function displaySearchResults(results) { + showSpinner(); + const resultsContainer = document.getElementById('search-results'); + resultsContainer.innerHTML = ''; + + results.forEach(item => { + const card = document.createElement('div'); + card.className = 'search-result-card'; + card.style.cursor = 'pointer'; + + const imagePath = item.poster_path || item.profile_path ? `https://image.tmdb.org/t/p/w500${item.poster_path || item.profile_path}` : null; + + if (imagePath) { + const image = document.createElement('img'); + image.src = imagePath; + image.className = 'result-image'; + card.appendChild(image); + } + else { + const placeholder = document.createElement('div'); + placeholder.className = 'result-image-placeholder'; + placeholder.textContent = 'Image Not Available'; + placeholder.style.textAlign = 'center'; + placeholder.style.padding = '10px'; + card.appendChild(placeholder); + } + + const details = document.createElement('div'); + details.className = 'result-details'; + + const name = document.createElement('div'); + name.className = 'result-name'; + name.textContent = item.title || item.name; + details.appendChild(name); + + const type = document.createElement('div'); + type.className = 'result-type'; + type.textContent = item.media_type === 'movie' ? 'Movie' : item.media_type === 'tv' ? 'TV Series' : 'Person'; + details.appendChild(type); + + card.appendChild(details); + resultsContainer.appendChild(card); + + card.addEventListener('click', () => handleResultClick(item)); + }); + + hideSpinner(); + } + + async function handleResultClick(item) { + console.log('Clicked item:', item.media_type, item.id); + + if (!item.media_type) { + console.log('Media type is undefined'); + return; + } + + if (item.media_type === 'movie') { + localStorage.setItem('selectedMovieId', item.id); + window.location.href = 'movie-details.html'; + } + else if (item.media_type === 'tv') { + localStorage.setItem('selectedTvSeriesId', item.id); + window.location.href = 'tv-details.html'; + } + else if (item.media_type === 'person') { + try { + const personDetailsUrl = `https://${getMovieVerseData()}/3/person/${item.id}?${generateMovieNames()}${getMovieCode()}`; + const response = await fetch(personDetailsUrl); + const personDetails = await response.json(); + + if (personDetails.known_for_department === 'Directing') { + localStorage.setItem('selectedDirectorId', item.id); + window.location.href = 'director-details.html?' + item.id; + } + else { + localStorage.setItem('selectedActorId', item.id); + window.location.href = 'actor-details.html?' + item.id; + } + } + catch (error) { + console.log('Error fetching person details:', error); + } + } + else { + console.log('Unknown media type:', item.media_type); + } + } +}); + +document.addEventListener('DOMContentLoaded', function() { + const searchInput = document.getElementById('search'); + const viewAllResultsBtn = document.getElementById('view-all-results'); + const clearSearchBtn = document.getElementById('clear-search'); + const searchResultsContainer = document.getElementById('search-results'); + let selectedIndex = -1; + + function clearSelection() { + const results = searchResultsContainer.getElementsByClassName('search-result-card'); + if (selectedIndex >= 0 && selectedIndex < results.length) { + results[selectedIndex].style.backgroundColor = ''; + } + else if (selectedIndex === results.length) { + viewAllResultsBtn.style.backgroundColor = ''; + } + else if (selectedIndex === results.length + 1) { + clearSearchBtn.style.backgroundColor = ''; + } + } + + function moveSelection(direction) { + const results = searchResultsContainer.getElementsByClassName('search-result-card'); + const totalElements = results.length + 2; + clearSelection(); + + if (direction === 'down') { + selectedIndex = (selectedIndex + 1) % totalElements; + } + else if (direction === 'up') { + selectedIndex = (selectedIndex - 1 + totalElements) % totalElements; + } + + if (selectedIndex < results.length) { + results[selectedIndex].style.backgroundColor = '#ff8623'; + results[selectedIndex].scrollIntoView({ block: "nearest" }); + } + else if (selectedIndex === results.length) { + viewAllResultsBtn.style.backgroundColor = '#ff8623'; + viewAllResultsBtn.scrollIntoView({ block: "nearest" }); + } + else if (selectedIndex === results.length + 1) { + clearSearchBtn.style.backgroundColor = '#ff8623'; + clearSearchBtn.scrollIntoView({ block: "nearest" }); + } + } + + searchInput.addEventListener('keydown', function(e) { + if (e.key === 'ArrowDown' || (e.key === 'Tab' && !e.shiftKey)) { + e.preventDefault(); + moveSelection('down'); + } + else if (e.key === 'ArrowUp' || (e.key === 'Tab' && e.shiftKey)) { + e.preventDefault(); + moveSelection('up'); + } + else if (e.key === 'Enter') { + e.preventDefault(); + if (selectedIndex >= 0 && selectedIndex < searchResultsContainer.getElementsByClassName('search-result-card').length) { + searchResultsContainer.getElementsByClassName('search-result-card')[selectedIndex].click(); + } + else if (selectedIndex === searchResultsContainer.getElementsByClassName('search-result-card').length) { + viewAllResultsBtn.click(); + } + else if (selectedIndex === searchResultsContainer.getElementsByClassName('search-result-card').length + 1) { + clearSearchBtn.click(); + } + else { + const query = searchInput.value.trim(); + localStorage.setItem('searchQuery', query); + window.location.href = 'search.html'; + } + } + }); + + searchInput.addEventListener('blur', clearSelection); +}); diff --git a/MovieVerse-Mobile/app/js/chat.js b/MovieVerse-Mobile/app/js/chat.js new file mode 100644 index 00000000..7c9f6f1c --- /dev/null +++ b/MovieVerse-Mobile/app/js/chat.js @@ -0,0 +1,470 @@ +import { initializeApp } from "https://www.gstatic.com/firebasejs/10.8.0/firebase-app.js"; +import { getFirestore, collection, addDoc, doc, startAfter, getDocs, query, where, orderBy, onSnapshot, 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)'; + document.getElementById('footer').style.display = 'none'; + + const adContainer2 = document.getElementById('ad-container2'); + if (adContainer2) { + document.body.insertBefore(signInMessage, adContainer2); + } + else { + document.body.appendChild(signInMessage); + } + } + else { + mainElement.style.display = ''; + } + + loadUserList(); + setupSearchListeners(); +}); + + +const firebaseConfig = { + 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'); +let selectedUserEmail = null; + +const messagesDiv = document.getElementById('messages'); +const userListDiv = document.getElementById('userList'); +const messageInput = document.getElementById('messageInput'); +const sendButton = document.getElementById('sendButton'); + +sendButton.addEventListener('click', async () => { + const text = messageInput.value.trim(); + if (text && selectedUserEmail) { + try { + await addDoc(collection(db, "messages"), { + sender: currentUserEmail, + recipient: selectedUserEmail, + message: text, + timestamp: serverTimestamp(), + readBy: [currentUserEmail] + }); + messageInput.value = ''; + + const userElement = document.querySelector(`.user[data-email="${selectedUserEmail}"]`); + + if (!userElement) { + const newUserElement = await createUserElement(selectedUserEmail); + userListDiv.prepend(newUserElement); + selectUser(newUserElement); + } + else { + userListDiv.prepend(userElement); + selectUser(userElement); + } + } + catch (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 profileSnapshot = await getDocs(profileQuery); + let imageUrl = '../../images/user-default.png'; + if (!profileSnapshot.empty) { + const profileData = profileSnapshot.docs[0].data(); + imageUrl = profileData.profileImage || imageUrl; + } + + const img = document.createElement('img'); + img.src = imageUrl; + img.style.width = '50px'; + img.style.borderRadius = '25px'; + img.style.marginRight = '10px'; + userElement.appendChild(img); + + const emailDiv = document.createElement('div'); + emailDiv.textContent = email; + userElement.appendChild(emailDiv); + + return userElement; +} + +function selectUser(userElement) { + if (previouslySelectedUserElement) { + previouslySelectedUserElement.classList.remove('selected'); + previouslySelectedUserElement.style.backgroundColor = ''; + } + 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(); + } +}); + +function formatMessage(message, isCurrentUser, timestamp) { + 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'; + } + + 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 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' + }); + + const tooltip = document.createElement('div'); + tooltip.className = 'tooltip'; + tooltip.textContent = tooltipText; + document.body.appendChild(tooltip); + + const rect = messageElement.getBoundingClientRect(); + const tooltipRect = tooltip.getBoundingClientRect(); + + const leftPosition = rect.left + (rect.width / 2) - (tooltipRect.width / 2); + 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); + setTimeout(removeTooltip, 5000); +} + +const chatSection = document.getElementById('chatSection'); +const noUserSelected = document.getElementById('noUserSelected'); + +chatSection.style.display = 'none'; +noUserSelected.style.display = 'flex'; + +async function loadMessages(userEmail) { + selectedUserEmail = userEmail; + messagesDiv.innerHTML = ''; + + chatSection.style.display = 'flex'; + noUserSelected.style.display = 'none'; + + 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}"]`); + if (selectedUser) { + selectedUser.classList.add('selected'); + } + + const messagesQuery = query( + collection(db, "messages"), + orderBy("timestamp"), + where("sender", "in", [currentUserEmail, selectedUserEmail]), + where("recipient", "in", [currentUserEmail, selectedUserEmail]) + ); + + onSnapshot(messagesQuery, (snapshot) => { + 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); + messagesDiv.appendChild(messageElement); + + if (!isCurrentUser && (!messageData.readBy || !messageData.readBy.includes(currentUserEmail))) { + updateReadStatus(doc.id); + } + }); + + messagesDiv.scrollTop = messagesDiv.scrollHeight; + }); +} + +async function updateReadStatus(messageId) { + const messageRef = doc(db, "messages", messageId); + await updateDoc(messageRef, { + readBy: arrayUnion(currentUserEmail) + }); +} + +let searchDebounceTimeout; +let lastVisible = null; +const initialFetchLimit = 5; +const maxTotalFetch = 20; + +function setupSearchListeners() { + const searchUserInput = document.getElementById('searchUserInput'); + const searchUserResults = document.getElementById('searchUserResults'); + + searchUserInput.addEventListener('input', () => { + clearTimeout(searchDebounceTimeout); + const searchText = searchUserInput.value.trim(); + + if (searchText) { + searchDebounceTimeout = setTimeout(() => { + lastVisible = null; + performSearch(searchText, true); + }, 300); + } + else { + searchUserResults.innerHTML = ''; + searchUserResults.style.display = 'none'; + } + }); +} + +async function performSearch(searchText, isNewSearch = false) { + const searchUserResults = document.getElementById('searchUserResults'); + + try { + showSpinner(); + + let userQuery = query( + collection(db, 'MovieVerseUsers'), + where('email', '>=', searchText), + where('email', '<=', searchText + '\uf8ff'), + orderBy('email'), + limit(initialFetchLimit) + ); + + if (!isNewSearch && lastVisible) { + userQuery = query(userQuery, startAfter(lastVisible)); + } + + const querySnapshot = await getDocs(userQuery); + + if (isNewSearch) { + searchUserResults.innerHTML = ''; + } + + if (!querySnapshot.empty) { + lastVisible = querySnapshot.docs[querySnapshot.docs.length - 1]; + } + + 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 profileSnapshot = await getDocs(profileQuery); + let imageUrl = '../../images/user-default.png'; + if (!profileSnapshot.empty) { + const profileData = profileSnapshot.docs[0].data(); + imageUrl = profileData.profileImage || imageUrl; + } + + const img = document.createElement('img'); + img.src = imageUrl; + 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'; + 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)); + searchUserResults.appendChild(loadMoreButton); + + if (searchUserResults.children.length >= maxTotalFetch) { + loadMoreButton.style.display = 'none'; + } + } + } + catch (error) { + 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!"; + noUserSelected.style.margin = '25px auto'; + } + hideSpinner(); + } + } +} + +let previouslySelectedUserElement = null; + +async function loadUserList() { + try { + showSpinner(); + + const userLimit = 5; + const messageLimit = 30; + + const sentMessagesQuery = query( + 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) + ); + + 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)); + + let users = []; + 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); + } + }); + } + } + + users.sort((a, b) => { + 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); + return (bLastMessage?.data().timestamp.toDate() || 0) - (aLastMessage?.data().timestamp.toDate() || 0); + }); + + users = users.slice(0, userLimit); + + userListDiv.innerHTML = ''; + for (const user of users) { + 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 = ''; + } + selectedUserEmail = user.email; + loadMessages(user.email); + 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; + } + + const img = document.createElement('img'); + img.src = imageUrl; + img.style.width = '50px'; + img.style.borderRadius = '25px'; + img.style.marginRight = '10px'; + userElement.appendChild(img); + + const emailDiv = document.createElement('div'); + emailDiv.textContent = user.email; + userElement.appendChild(emailDiv); + + userListDiv.appendChild(userElement); + } + + hideSpinner(); + } + catch (error) { + 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!"; + } + hideSpinner(); + } + } +} + +function showSpinner() { + document.getElementById('myModal').classList.add('modal-visible'); +} + +function hideSpinner() { + document.getElementById('myModal').classList.remove('modal-visible'); +} diff --git a/MovieVerse-Mobile/app/js/chatbot.js b/MovieVerse-Mobile/app/js/chatbot.js index f915220f..800a1f5f 100644 --- a/MovieVerse-Mobile/app/js/chatbot.js +++ b/MovieVerse-Mobile/app/js/chatbot.js @@ -52,7 +52,7 @@ async function fetchGenreMap() { localStorage.setItem('genreMap', JSON.stringify(genreMap)); } catch (error) { - console.error('Error fetching genre map:', error); + console.log('Error fetching genre map:', error); } } @@ -84,7 +84,7 @@ async function rotateUserStats() { { label: "Favorite Movies", getValue: () => { - const favoritedMovies = JSON.parse(localStorage.getItem('favoritesMovies')) || []; + const favoritedMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; return favoritedMovies.length; } }, @@ -92,8 +92,37 @@ async function rotateUserStats() { label: "Favorite Genre", getValue: () => { const mostCommonGenreCode = getMostCommonGenre(); - const genreMap = JSON.parse(localStorage.getItem('genreMap')) || {}; - return genreMap[mostCommonGenreCode] || 'Not Available'; + const genreMapString = localStorage.getItem('genreMap'); + if (!genreMapString) { + 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'; + } + + let genreObject; + if (Array.isArray(genreMap)) { + genreObject = genreMap.reduce((acc, genre) => { + acc[genre.id] = genre.name; + return acc; + }, {}); + } + 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'; + } + + return genreObject[mostCommonGenreCode] || 'Not Available'; } }, { label: "Watchlists Created", getValue: () => localStorage.getItem('watchlistsCreated') || 0 }, @@ -424,7 +453,7 @@ async function fetchAndRedirectToMovieDetails(movieName) { } } catch (error) { - console.error('Error fetching movie details:', error); + console.log('Error fetching movie details:', error); alert('Failed to fetch movie details. Please try again later.'); } } @@ -449,12 +478,13 @@ async function fetchMovieTrailer(movieName) { } } catch (error) { - console.error('Error fetching movie trailer:', error); + console.log('Error fetching movie trailer:', error); } } async function getTrailerUrl(movieId) { const trailerApiUrl = `https://${getMovieVerseData()}/3/movie/${movieId}/videos?${generateMovieNames()}${getMovieCode()}`; + try { const response = await fetch(trailerApiUrl); const data = await response.json(); @@ -462,7 +492,7 @@ async function getTrailerUrl(movieId) { return trailer ? `https://www.youtube.com/watch?v=${trailer.key}` : null; } catch (error) { - console.error('Error fetching trailer:', error); + console.log('Error fetching trailer:', error); return null; } } @@ -485,6 +515,7 @@ async function fetchPersonDetails(name, type) { const response = await fetch(searchUrl); const data = await response.json(); const person = data.results[0]; + if (person) { localStorage.setItem(type === 'director' ? 'selectedDirectorId' : 'selectedActorId', person.id); window.location.href = type === 'director' ? 'director-details.html' : 'actor-details.html'; @@ -494,7 +525,7 @@ async function fetchPersonDetails(name, type) { } } catch (error) { - console.error(`Error fetching ${type} details:`, error); + console.log(`Error fetching ${type} details:`, error); alert(`Failed to fetch ${type} details. Please try again later.`); } } @@ -505,6 +536,7 @@ async function fetchCompanyDetails(companyName) { const response = await fetch(searchUrl); const data = await response.json(); const company = data.results[0]; + if (company) { localStorage.setItem('selectedCompanyId', company.id); window.location.href = 'company-details.html'; @@ -514,13 +546,14 @@ async function fetchCompanyDetails(companyName) { } } catch (error) { - console.error('Error fetching company details:', error); + console.log('Error fetching company details:', error); alert('Failed to fetch company details. Please try again later.'); } } async function movieVerseResponse(message) { const lowerMessage = message.toLowerCase(); + if (lowerMessage.startsWith("do you know about ") || lowerMessage.startsWith("tell me about ") || lowerMessage.startsWith("what is ")) { const movieName = lowerMessage.replace(/^(do you know about|show me|tell me about|what is) /, ''); return await fetchMovieDetailsFromTMDB(movieName); @@ -552,161 +585,258 @@ async function movieVerseResponse(message) { } if (lowerMessage.includes("hello") || lowerMessage.includes("hi") || lowerMessage.includes("hey")) { return "Hello! How can I assist you with MovieVerse today?"; - } else if (lowerMessage.includes("bye") || lowerMessage.includes("goodbye")) { + } + else if (lowerMessage.includes("bye") || lowerMessage.includes("goodbye")) { return "Goodbye! Thank you for using MovieVerse Assistant and have a nice day!"; - } else if (lowerMessage.includes("how are you")) { + } + else if (lowerMessage.includes("how are you")) { return "I'm your digital MovieVerse assistant, ready to help! How can I assist you with movie info?"; - } else if (lowerMessage.includes("search movie")) { + } + else if (lowerMessage.includes("search movie")) { return "To find information about a movie, please provide its name or keyword related to it."; - } else if (lowerMessage.includes("imdb rating")) { + } + else if (lowerMessage.includes("imdb rating")) { return "You can search for a movie, and I'll provide its IMDb rating among other details. Please provide the movie name!"; - } else if (lowerMessage.includes("movie description") || lowerMessage.includes("tell me about")) { + } + else if (lowerMessage.includes("movie description") || lowerMessage.includes("tell me about")) { return "Sure, please provide the movie name you want to learn about, and I'll fetch its description for you!"; - } else if (lowerMessage.includes("how many movies")) { + } + else if (lowerMessage.includes("how many movies")) { return "MovieVerse has a vast MovieVerse-Databases of millions of movies. You can search for any movie, and I'll try to fetch details for you!"; - } else if (lowerMessage.includes("latest movies")) { + } + else if (lowerMessage.includes("latest movies")) { return "I can provide information on recent movie releases. However, for the most up-to-date releases, consider checking the 'Latest Movies' section of MovieVerse!"; - } else if (lowerMessage.includes("recommend a movie") || lowerMessage.includes("suggestion")) { + } + else if (lowerMessage.includes("recommend a movie") || lowerMessage.includes("suggestion")) { return "Certainly! How about watching 'Inception'? It's a critically acclaimed movie with a captivating plot!"; - } else if (lowerMessage.includes("how does this work") || lowerMessage.includes("how to use")) { + } + else if (lowerMessage.includes("how does this work") || lowerMessage.includes("how to use")) { return "Simply type in your query related to a movie, and I'll provide details from our MovieVerse MovieVerse-Databases. You can ask about IMDb ratings, descriptions, and more!"; - } else if (lowerMessage.includes("who created this") || lowerMessage.includes("developer")) { + } + else if (lowerMessage.includes("who created this") || lowerMessage.includes("developer")) { return "MovieVerse is the result of the hard work of dedicated developers passionate about movies. We hope you find it helpful!"; - } else if (lowerMessage.includes("top rated movies")) { + } + else if (lowerMessage.includes("top rated movies")) { return "Our top-rated movies include 'The Shawshank Redemption', 'The Godfather', and 'The Dark Knight'. Would you like a detailed list?"; - } else if (lowerMessage.includes("genre")) { + } + else if (lowerMessage.includes("genre")) { return "We have movies spanning various genres: Action, Drama, Comedy, Romance, Thriller, and more! Which genre are you interested in?"; - } else if (lowerMessage.includes("actor") || lowerMessage.includes("actress")) { + } + else if (lowerMessage.includes("actor") || lowerMessage.includes("actress")) { return "Sure, which actor or actress are you interested in? Provide a name, and I'll fetch the movies they've starred in!"; - } else if (lowerMessage.includes("director")) { + } + else if (lowerMessage.includes("director")) { return "Great! Which director's filmography are you interested in?"; - } else if (lowerMessage.includes("animated movies")) { + } + else if (lowerMessage.includes("animated movies")) { return "We have a wide collection of animated movies! Some popular ones include 'Toy Story', 'Frozen', and 'Spirited Away'."; - } else if (lowerMessage.includes("thank you") || lowerMessage.includes("thanks")) { + } + else if (lowerMessage.includes("thank you") || lowerMessage.includes("thanks")) { return "You're welcome! If you have any more questions, feel free to ask. Enjoy your movie experience!"; - } else if (lowerMessage.includes("subscription") || lowerMessage.includes("membership")) { + } + else if (lowerMessage.includes("subscription") || lowerMessage.includes("membership")) { return "MovieVerse offers different subscription tiers. For detailed information, you might want to check our 'Subscription' section."; - } else if (lowerMessage.includes("watch movie")) { + } + else if (lowerMessage.includes("watch movie")) { return "While MovieVerse provides detailed information on movies, to watch them, you might need to visit specific streaming platforms or theaters!"; - } else if (lowerMessage.includes("are you a bot")) { + } + else if (lowerMessage.includes("are you a bot")) { return "Yes, I'm the MovieVerse digital assistant. How can I help you further?"; - } else if (lowerMessage.includes("documentary")) { + } + else if (lowerMessage.includes("documentary")) { return "We have an extensive collection of documentaries. From nature to history and science, what topic interests you?"; - } else if (lowerMessage.includes("foreign films")) { + } + else if (lowerMessage.includes("foreign films")) { return "MovieVerse has films from all around the world. Looking for any specific region or language?"; - } else if (lowerMessage.includes("classic movies")) { + } + else if (lowerMessage.includes("classic movies")) { return "Ah, classics! Some all-time favorites include 'Casablanca', 'Gone with the Wind', and 'Citizen Kane'. Would you like more recommendations?"; - } else if (lowerMessage.includes("family movies")) { + } + else if (lowerMessage.includes("family movies")) { return "We have plenty of family-friendly movies. 'The Lion King', 'Finding Nemo', and 'Toy Story' are a few favorites. Looking for anything specific?"; - } else if (lowerMessage.includes("comedy")) { + } + else if (lowerMessage.includes("comedy")) { return "In need of a good laugh? We've got comedies like 'Dumb and Dumber', 'Bridesmaids', and 'Anchorman' to name a few!"; - } else if (lowerMessage.includes("action movies")) { + } + else if (lowerMessage.includes("action movies")) { return "For adrenaline junkies, we've got action-packed movies like 'Die Hard', 'Mad Max: Fury Road', and 'The Dark Knight'. Ready to dive in?"; - } else if (lowerMessage.includes("horror")) { + } + else if (lowerMessage.includes("horror")) { return "Looking for a scare? Consider watching 'The Exorcist', 'Psycho', or 'Get Out'. Don't forget to keep the lights on!"; - } else if (lowerMessage.includes("romance")) { + } + else if (lowerMessage.includes("romance")) { return "Feeling romantic? Check out 'The Notebook', 'Pride and Prejudice', or 'Before Sunrise'. Love is in the air!"; - } else if (lowerMessage.includes("sci-fi")) { + } + else if (lowerMessage.includes("sci-fi")) { return "For sci-fi enthusiasts, we recommend 'Blade Runner', 'Star Wars', or 'Interstellar'. Ready to travel through space and time?"; - } else if (lowerMessage.includes("trailers")) { + } + else if (lowerMessage.includes("trailers")) { return "Want to see what's coming up? Our 'Trailers' section has the latest teasers and previews of upcoming films!"; - } else if (lowerMessage.includes("membership benefits")) { + } + else if (lowerMessage.includes("membership benefits")) { return "Members get exclusive access to early releases, high-definition streaming, and ad-free experience. Interested in upgrading?"; - } else if (lowerMessage.includes("create an account")) { + } + else if (lowerMessage.includes("create an account")) { return "Creating an account is easy! Just head to the 'Sign Up' section on our website and follow the steps."; - } else if (lowerMessage.includes("forgot password")) { + } + else if (lowerMessage.includes("forgot password")) { return "No worries! Just click on the 'Forgot Password' link on the login page, and we'll guide you through the reset process."; - } else if (lowerMessage.includes("movie ratings")) { + } + else if (lowerMessage.includes("movie ratings")) { return "Our ratings are sourced from critics and viewers like you. They provide a combined score to help you pick the best movies!"; - } else if (lowerMessage.includes("how do you work")) { + } + else if (lowerMessage.includes("how do you work")) { return "I'm here to answer your questions about MovieVerse and movies in general. Just ask away!"; - } else if (lowerMessage.includes("are you real")) { + } + else if (lowerMessage.includes("are you real")) { return "I'm a virtual assistant powered by code. While I'm not real, I'm here to help!"; - } else if (lowerMessage.includes("oscar winners")) { + } + else if (lowerMessage.includes("oscar winners")) { return "Looking for Oscar winners? We have a dedicated section for 'Academy Award Winners'. Check it out for a list of acclaimed films!"; - } else if (lowerMessage.includes("in theaters now")) { + } + else if (lowerMessage.includes("in theaters now")) { return "Our 'Now Showing' section provides a list of movies currently playing in theaters. Planning a movie outing?"; - } else if (lowerMessage.includes("coming soon")) { + } + else if (lowerMessage.includes("coming soon")) { return "Anticipating new releases? Head over to our 'Coming Soon' tab to check out movies hitting the theaters soon!"; - } else if (lowerMessage.includes("movie runtime")) { + } + else if (lowerMessage.includes("movie runtime")) { return "Please specify the movie you're inquiring about, and I'll fetch its runtime for you!"; - } else if (lowerMessage.includes("indie films")) { + } + else if (lowerMessage.includes("indie films")) { return "Indie films offer unique storytelling. Some of our favorites include 'Moonlight', 'Lady Bird', and 'Whiplash'. Would you like to explore more indie titles?"; - } else if (lowerMessage.includes("film festivals")) { + } + else if (lowerMessage.includes("film festivals")) { return "We have a collection of movies that have made waves in film festivals. From Cannes to Sundance, which festival's winners are you interested in?"; - } else if (lowerMessage.includes("animation studios")) { + } + else if (lowerMessage.includes("animation studios")) { return "From Pixar to Studio Ghibli, we have movies from renowned animation studios. Any particular studio you're fond of?"; - } else if (lowerMessage.includes("musicals")) { + } + else if (lowerMessage.includes("musicals")) { return "Sing your heart out! 'La La Land', 'The Greatest Showman', or classics like 'The Sound of Music' are all available. Ready for a song and dance?"; - } else if (lowerMessage.includes("kid movies")) { + } + else if (lowerMessage.includes("kid movies")) { return "For the little ones, we have 'Despicable Me', 'Frozen', and many more. Anything in particular they enjoy?"; - } else if (lowerMessage.includes("adaptations")) { + } + else if (lowerMessage.includes("adaptations")) { return "Books turned movies? We have 'Harry Potter', 'The Hunger Games', and classics like 'To Kill a Mockingbird'. Interested in a specific adaptation?"; - } else if (lowerMessage.includes("based on true stories")) { + } + else if (lowerMessage.includes("based on true stories")) { return "The truth can be stranger than fiction! Check out 'The Imitation Game', 'Schindler's List', or 'Catch Me If You Can'. Any specific era or event you're interested in?"; - } else if (lowerMessage.includes("customer support")) { + } + else if (lowerMessage.includes("customer support")) { return "Having issues? Our customer support team is here to help. You can reach out via the 'Support' section on our website."; - } else if (lowerMessage.includes("subscription cancel")) { + } + else if (lowerMessage.includes("subscription cancel")) { return "We're sad to see you go. To cancel your subscription, please go to the 'Account Settings' section."; - } else if (lowerMessage.includes("refunds")) { + } + else if (lowerMessage.includes("refunds")) { return "For refund queries, please get in touch with our customer support. They'll guide you through the process."; - } else if (lowerMessage.includes("device compatibility")) { + } + else if (lowerMessage.includes("device compatibility")) { return "MovieVerse is compatible with a range of devices, from smartphones and tablets to desktops and smart TVs. Any specific device you're asking about?"; - } else if (lowerMessage.includes("movie suggestions based on mood")) { + } + else if (lowerMessage.includes("movie suggestions based on mood")) { return "Of course! Let me know your mood, and I'll suggest a movie accordingly!"; - } else if (lowerMessage.includes("movie for date night")) { + } + else if (lowerMessage.includes("movie for date night")) { return "How about a romantic comedy? 'Pride & Prejudice' or something light-hearted like '500 Days of Summer'?"; - } else if (lowerMessage.includes("is there a series section")) { + } + else if (lowerMessage.includes("is there a series section")) { return "Yes, apart from movies, we also have a collection of TV series. From 'Breaking Bad' to 'Stranger Things', binge away!"; - } else if (lowerMessage.includes("award-winning movies")) { + } + else if (lowerMessage.includes("award-winning movies")) { return "Looking for critically acclaimed cinema? Check our 'Award Winners' section for movies that have received major accolades!"; - } else if (lowerMessage.includes("do you have classics from the 80s")) { + } + else if (lowerMessage.includes("do you have classics from the 80s")) { return "Absolutely! The 80s were iconic. Dive into classics like 'E.T.', 'The Breakfast Club', or 'Back to the Future'. Ready for some nostalgia?"; - } else if (lowerMessage.includes("movie suggestions based on genre")) { + } + else if (lowerMessage.includes("movie suggestions based on genre")) { return "Sure! Let me know your favorite genre, and I'll suggest some movies accordingly!"; - } else if (lowerMessage.includes("movie suggestions based on actor")) { + } + else if (lowerMessage.includes("movie suggestions based on actor")) { return "Of course! Let me know your favorite actor, and I'll suggest some movies accordingly!"; - } else if (lowerMessage.includes("movie suggestions based on director")) { + } + else if (lowerMessage.includes("movie suggestions based on director")) { return "Of course! Let me know your favorite director, and I'll suggest some movies accordingly!"; - } else if (lowerMessage.includes("movie suggestions based on year")) { + } + else if (lowerMessage.includes("movie suggestions based on year")) { return "Of course! Let me know your favorite year, and I'll suggest some movies accordingly!"; - } else if (lowerMessage.includes("movie") || lowerMessage.includes("movies")) { + } + else if (lowerMessage.includes("movie") || lowerMessage.includes("movies")) { return "You can search for a movie using the search field above!"; - } else if (lowerMessage.includes("1900s")) { + } + else if (lowerMessage.includes("1900s")) { return "Movies in the 1900s include: A Trip to the Moon, The Great Train Robbery, etc."; - } else if (lowerMessage.includes("1910s")) { + } + else if (lowerMessage.includes("1910s")) { return "Movies in the 1910s include: The Birth of a Nation, Intolerance, etc."; - } else if (lowerMessage.includes("1920s")) { + } + else if (lowerMessage.includes("1920s")) { return "Movies in the 1920s include: The Kid, The Gold Rush, etc."; - } else if (lowerMessage.includes("1930s")) { + } + else if (lowerMessage.includes("1930s")) { return "Movies in the 1930s include: King Kong, Snow White and the Seven Dwarfs, etc."; - } else if (lowerMessage.includes("1940s")) { + } + else if (lowerMessage.includes("1940s")) { return "Movies in the 1940s include: Citizen Kane, Casablanca, etc."; - } else if (lowerMessage.includes("1950s")) { + } + else if (lowerMessage.includes("1950s")) { return "Movies in the 1950s include: Sunset Boulevard, Singin' in the Rain, etc."; - } else if (lowerMessage.includes("1960s")) { + } + else if (lowerMessage.includes("1960s")) { return "Movies in the 1960s include: Psycho, The Apartment, etc."; - } else if (lowerMessage.includes("1970s")) { + } + else if (lowerMessage.includes("1970s")) { return "Movies in the 1970s include: The Godfather, Star Wars, etc."; - } else if (lowerMessage.includes("1980s")) { + } + else if (lowerMessage.includes("1980s")) { return "Movies in the 1980s include: Back to the Future, The Shining, etc."; - } else if (lowerMessage.includes("1990s")) { + } + else if (lowerMessage.includes("1990s")) { return "Movies in the 1990s include: The Silence of the Lambs, Titanic, etc."; - } else if (lowerMessage.includes("2000s")) { + } + else if (lowerMessage.includes("2000s")) { return "Movies in the 2000s include: The Lord of the Rings: The Return of the King, The Dark Knight, etc."; - } else if (lowerMessage.includes("2010s")) { + } + else if (lowerMessage.includes("2010s")) { return "Movies in the 2010s include: Inception, The Avengers, etc."; - } else if (lowerMessage.includes("2020s")) { + } + else if (lowerMessage.includes("2020s")) { return "Movies in the 2020s include: Tenet, Soul, etc."; - } else if (lowerMessage.includes("2022")) { + } + else if (lowerMessage.includes("2022")) { return "Movies in 2022 include: Thor: Love and Thunder, Doctor Strange in the Multiverse of Madness, etc."; - } else if (lowerMessage.includes("2023")) { + } + else if (lowerMessage.includes("2023")) { return "Movies in 2023 include: The Flash, Black Panther: Wakanda Forever, etc."; - } else if (lowerMessage.includes("2024")) { + } + else if (lowerMessage.includes("2024")) { return "Movies in 2024 include: Indiana Jones 5, The Batman, etc."; - } else if (lowerMessage.includes("movieverse analytics") || lowerMessage.includes("movieverse stats") || lowerMessage.includes("movieverse insights")) { + } + else if (lowerMessage.includes("movieverse analytics") || lowerMessage.includes("movieverse stats") || lowerMessage.includes("movieverse insights")) { return "MovieVerse Analytics provides insights into user activity, popular movies, and more. You can access it by pressing the About button on the top right, then selecting MovieVerse Analytics at the bottom of the page."; - } else { + } + else if (lowerMessage.includes("most visited director")) { + return `The most visited director is ${getMostVisitedDirector()}.`; + } + else if (lowerMessage.includes("trivia accuracy")) { + return `Your trivia accuracy is ${getTriviaAccuracy()}.`; + } + else if (lowerMessage.includes("most common genre") || lowerMessage.includes("favorite genre")) { + return `Your most common genre is ${getMostCommonGenre()}.`; + } + else if (lowerMessage.includes("movie of the day")) { + showMovieOfTheDay(); + return "Searching for the movie of the day. Please wait..."; + } + else if (lowerMessage.includes("sign in") || lowerMessage.includes("sign out")) { + return "Please click the Sign In/Out button at the top to sign in or out."; + } + else if (lowerMessage.includes("sign up")) { + return "Please click the Sign In button at the top to create an account."; + } + else { return "Sorry, I didn't catch that or find any movies with that name in our databases. Can you rephrase, check your spelling, or ask another question?"; } } @@ -715,9 +845,11 @@ const movieee = `https://${getMovieVerseData()}/3`; async function fetchMovieDetailsFromTMDB(movieName) { const url = `${movieee}/search/movie?${generateMovieNames()}${getMovieCode()}&query=${encodeURIComponent(movieName)}`; + try { const response = await fetch(url); const data = await response.json(); + if (data.results.length > 0) { const movie = data.results[0]; localStorage.setItem('selectedMovieId', movie.id); @@ -731,7 +863,7 @@ async function fetchMovieDetailsFromTMDB(movieName) { } } catch (error) { - console.error('Error fetching movie details:', error); + console.log('Error fetching movie details:', error); return "Sorry, I encountered an error while trying to fetch movie details. Please try again later."; } } @@ -755,50 +887,11 @@ async function showMovieOfTheDay() { } } catch (error) { - console.error('Error fetching movie:', error); + console.log('Error fetching movie:', error); fallbackMovieSelection(); } } -function handleSignInOut() { - const isSignedIn = JSON.parse(localStorage.getItem('isSignedIn')) || false; - - if (isSignedIn) { - localStorage.setItem('isSignedIn', JSON.stringify(false)); - alert('You have been signed out.'); - } - else { - window.location.href = 'sign-in.html'; - return; - } - - updateSignInButtonState(); -} - -function updateSignInButtonState() { - const isSignedIn = JSON.parse(localStorage.getItem('isSignedIn')) || false; - - const signInText = document.getElementById('signInOutText'); - const signInIcon = document.getElementById('signInIcon'); - const signOutIcon = document.getElementById('signOutIcon'); - - if (isSignedIn) { - signInText.textContent = 'Sign Out'; - signInIcon.style.display = 'none'; - signOutIcon.style.display = 'inline-block'; - } - else { - signInText.textContent = 'Sign In'; - signInIcon.style.display = 'inline-block'; - signOutIcon.style.display = 'none'; - } -} - -document.addEventListener("DOMContentLoaded", function() { - updateSignInButtonState(); - document.getElementById('googleSignInBtn').addEventListener('click', handleSignInOut); -}); - 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]; const randomFallbackMovie = fallbackMovies[Math.floor(Math.random() * fallbackMovies.length)]; diff --git a/MovieVerse-Mobile/app/js/christopher-nolan.js b/MovieVerse-Mobile/app/js/christopher-nolan.js index 6d9a9cfd..1424a4dc 100644 --- a/MovieVerse-Mobile/app/js/christopher-nolan.js +++ b/MovieVerse-Mobile/app/js/christopher-nolan.js @@ -225,7 +225,7 @@ async function fetchDirectorDetails(directorId) { } } catch (error) { - console.error('Error fetching director details:', error); + console.log('Error fetching director details:', error); document.getElementById('director-details-container').innerHTML = '

Error fetching director details

'; } } @@ -320,7 +320,7 @@ async function fetchGenreMap() { localStorage.setItem('genreMap', JSON.stringify(genreMap)); } catch (error) { - console.error('Error fetching genre map:', error); + console.log('Error fetching genre map:', error); } } @@ -352,7 +352,7 @@ async function rotateUserStats() { { label: "Favorite Movies", getValue: () => { - const favoritedMovies = JSON.parse(localStorage.getItem('favoritesMovies')) || []; + const favoritedMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; return favoritedMovies.length; } }, @@ -360,8 +360,37 @@ async function rotateUserStats() { label: "Favorite Genre", getValue: () => { const mostCommonGenreCode = getMostCommonGenre(); - const genreMap = JSON.parse(localStorage.getItem('genreMap')) || {}; - return genreMap[mostCommonGenreCode] || 'Not Available'; + const genreMapString = localStorage.getItem('genreMap'); + if (!genreMapString) { + 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'; + } + + let genreObject; + if (Array.isArray(genreMap)) { + genreObject = genreMap.reduce((acc, genre) => { + acc[genre.id] = genre.name; + return acc; + }, {}); + } + 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'; + } + + return genreObject[mostCommonGenreCode] || 'Not Available'; } }, { label: "Watchlists Created", getValue: () => localStorage.getItem('watchlistsCreated') || 0 }, diff --git a/MovieVerse-Mobile/app/js/comments-tv.js b/MovieVerse-Mobile/app/js/comments-tv.js new file mode 100644 index 00000000..9b7044a7 --- /dev/null +++ b/MovieVerse-Mobile/app/js/comments-tv.js @@ -0,0 +1,161 @@ +import { initializeApp } from "https://www.gstatic.com/firebasejs/10.8.0/firebase-app.js"; +import { getFirestore, collection, addDoc, getDocs, query, orderBy, where } from "https://www.gstatic.com/firebasejs/10.8.0/firebase-firestore.js"; +import { app, db } from './firebase.js'; + +const commentForm = document.getElementById("comment-form"); +commentForm.addEventListener("submit", async (e) => { + e.preventDefault(); + const userName = document.getElementById("user-name").value; + const userComment = document.getElementById("user-comment").value; + const commentDate = new Date(); + const tvSeriesId = localStorage.getItem("selectedTvSeriesId"); + + try { + await addDoc(collection(db, "comments"), { + userName, + userComment, + commentDate, + tvSeriesId + }); + commentForm.reset(); + fetchComments(); + } + catch (error) { + console.log("Error adding comment: ", error); + } +}); + +let modal = document.getElementById("comment-modal"); +let btn = document.getElementById("toggle-comment-modal"); +let span = document.getElementsByClassName("close")[0]; + +btn.onclick = function() { + modal.style.display = "block"; +} + +span.onclick = function() { + modal.style.display = "none"; +} + +document.getElementById("post-comment-btn").onclick = function() { + modal.style.display = "none"; + +} + +window.onclick = function(event) { + if (event.target == modal) { + modal.style.display = "none"; + } +} + + +let currentPage = 1; +const commentsPerPage = 3; +let totalComments = 0; +let totalPages = 1; + +async function fetchComments() { + try { + const commentsContainer = document.getElementById("comments-container"); + commentsContainer.innerHTML = ''; + commentsContainer.style.maxWidth = "100%"; + const movieId = localStorage.getItem("selectedTvSeriesId"); + + const q = query(collection(db, "comments"), where("tvSeriesId", "==", movieId), orderBy("commentDate", "desc")); + const querySnapshot = await getDocs(q); + + totalComments = querySnapshot.size; + totalPages = Math.ceil(totalComments / commentsPerPage); + + let index = 0; + let displayedComments = 0; + + if (querySnapshot.empty) { + const noCommentsMsg = document.createElement("p"); + noCommentsMsg.textContent = "No comments for this movie/TV show yet."; + commentsContainer.appendChild(noCommentsMsg); + } else { + querySnapshot.forEach((doc) => { + if (index >= (currentPage - 1) * commentsPerPage && displayedComments < commentsPerPage) { + const comment = doc.data(); + const commentDate = comment.commentDate.toDate(); + + const formattedDate = formatCommentDate(commentDate); + const formattedTime = formatAMPM(commentDate); + + const timezoneOffset = -commentDate.getTimezoneOffset() / 60; + const utcOffset = timezoneOffset >= 0 ? `UTC+${timezoneOffset}` : `UTC${timezoneOffset}`; + const commentElement = document.createElement("div"); + + commentElement.title = `Posted at ${formattedTime} ${utcOffset}`; + const commentStyle = ` + max-width: 100%; + word-wrap: break-word; + overflow-wrap: break-word; + margin-bottom: 1rem; // Add some space between comments + `; + commentElement.style.cssText = commentStyle; + commentElement.innerHTML = ` +

+ ${comment.userName} on ${formattedDate}: + ${comment.userComment} +

+ `; + commentsContainer.appendChild(commentElement); + displayedComments++; + } + index++; + }); + } + + document.getElementById("prev-page").disabled = currentPage <= 1; + document.getElementById("next-page").disabled = currentPage >= totalPages; + } + catch (error) { + console.error("Error fetching user list: ", error); + if (error.code === 'resource-exhausted') { + const noUserSelected = document.getElementById('comments-section'); + if (noUserSelected) { + noUserSelected.innerHTML = "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!"; + noUserSelected.style.height = '350px'; + noUserSelected.style.textAlign = 'center'; + noUserSelected.style.maxWidth = '350px'; + } + hideSpinner(); + } + } +} + +function formatCommentDate(commentDate) { + const formattedDate = commentDate.toLocaleString('default', { month: 'short' }) + " " + + commentDate.getDate() + "th, " + + commentDate.getFullYear(); + return formattedDate; +} + +function formatAMPM(date) { + let hours = date.getHours(); + let minutes = date.getMinutes(); + const ampm = hours >= 12 ? 'PM' : 'AM'; + hours = hours % 12; + hours = hours || 12; + minutes = minutes < 10 ? '0' + minutes : minutes; + const strTime = hours + ':' + minutes + ' ' + ampm; + return strTime; +} + +document.getElementById("prev-page").addEventListener("click", () => { + if (currentPage > 1) { + currentPage--; + fetchComments(); + } +}); + +document.getElementById("next-page").addEventListener("click", () => { + if (currentPage < totalPages) { + currentPage++; + fetchComments(); + } +}); + +fetchComments(); diff --git a/MovieVerse-Mobile/app/js/comments.js b/MovieVerse-Mobile/app/js/comments.js index 6d879f8a..ba5dc79a 100644 --- a/MovieVerse-Mobile/app/js/comments.js +++ b/MovieVerse-Mobile/app/js/comments.js @@ -1,17 +1,6 @@ import { initializeApp } from "https://www.gstatic.com/firebasejs/10.8.0/firebase-app.js"; import { getFirestore, collection, addDoc, getDocs, query, orderBy, where } from "https://www.gstatic.com/firebasejs/10.8.0/firebase-firestore.js"; - -const firebaseConfig = { - apiKey: "AIzaSyDL6kQnSfUdD8Ut8HFrp9kuivqz1xdXm7k", - authDomain: "movieverse-app.firebaseapp.com", - projectId: "movieverse-app", - storageBucket: "movieverse-app.appspot.com", - messagingSenderId: "802943718871", - appId: "1:802943718871:web:48bc916cc99e2724212792" -}; - -const app = initializeApp(firebaseConfig); -const db = getFirestore(app); +import { app, db } from './firebase.js'; const commentForm = document.getElementById("comment-form"); commentForm.addEventListener("submit", async (e) => { @@ -32,13 +21,13 @@ commentForm.addEventListener("submit", async (e) => { fetchComments(); } catch (error) { - console.error("Error adding comment: ", error); + console.log("Error adding comment: ", error); } }); -var modal = document.getElementById("comment-modal"); -var btn = document.getElementById("toggle-comment-modal"); -var span = document.getElementsByClassName("close")[0]; +let modal = document.getElementById("comment-modal"); +let btn = document.getElementById("toggle-comment-modal"); +let span = document.getElementsByClassName("close")[0]; btn.onclick = function() { modal.style.display = "block"; @@ -66,61 +55,75 @@ let totalComments = 0; let totalPages = 1; async function fetchComments() { - const commentsContainer = document.getElementById("comments-container"); - commentsContainer.innerHTML = ''; - commentsContainer.style.maxWidth = "100%"; - const movieId = localStorage.getItem("selectedMovieId"); - - const q = query(collection(db, "comments"), where("movieId", "==", movieId), orderBy("commentDate", "desc")); - const querySnapshot = await getDocs(q); - - totalComments = querySnapshot.size; - totalPages = Math.ceil(totalComments / commentsPerPage); - - let index = 0; - let displayedComments = 0; - - if (querySnapshot.empty) { - const noCommentsMsg = document.createElement("p"); - noCommentsMsg.textContent = "No comments for this movie/TV show yet."; - commentsContainer.appendChild(noCommentsMsg); - } - else { - querySnapshot.forEach((doc) => { - if (index >= (currentPage - 1) * commentsPerPage && displayedComments < commentsPerPage) { - const comment = doc.data(); - const commentDate = comment.commentDate.toDate(); - - const formattedDate = formatCommentDate(commentDate); - const formattedTime = formatAMPM(commentDate); - - const timezoneOffset = -commentDate.getTimezoneOffset() / 60; - const utcOffset = timezoneOffset >= 0 ? `UTC+${timezoneOffset}` : `UTC${timezoneOffset}`; - const commentElement = document.createElement("div"); - - commentElement.title = `Posted at ${formattedTime} ${utcOffset}`; - const commentStyle = ` + try { + const commentsContainer = document.getElementById("comments-container"); + commentsContainer.innerHTML = ''; + commentsContainer.style.maxWidth = "100%"; + const movieId = localStorage.getItem("selectedMovieId"); + + const q = query(collection(db, "comments"), where("movieId", "==", movieId), orderBy("commentDate", "desc")); + const querySnapshot = await getDocs(q); + + totalComments = querySnapshot.size; + totalPages = Math.ceil(totalComments / commentsPerPage); + + let index = 0; + let displayedComments = 0; + + if (querySnapshot.empty) { + const noCommentsMsg = document.createElement("p"); + noCommentsMsg.textContent = "No comments for this movie/TV show yet."; + commentsContainer.appendChild(noCommentsMsg); + } else { + querySnapshot.forEach((doc) => { + if (index >= (currentPage - 1) * commentsPerPage && displayedComments < commentsPerPage) { + const comment = doc.data(); + const commentDate = comment.commentDate.toDate(); + + const formattedDate = formatCommentDate(commentDate); + const formattedTime = formatAMPM(commentDate); + + const timezoneOffset = -commentDate.getTimezoneOffset() / 60; + const utcOffset = timezoneOffset >= 0 ? `UTC+${timezoneOffset}` : `UTC${timezoneOffset}`; + const commentElement = document.createElement("div"); + + commentElement.title = `Posted at ${formattedTime} ${utcOffset}`; + const commentStyle = ` max-width: 100%; word-wrap: break-word; overflow-wrap: break-word; margin-bottom: 1rem; // Add some space between comments `; - commentElement.style.cssText = commentStyle; - commentElement.innerHTML = ` + commentElement.style.cssText = commentStyle; + commentElement.innerHTML = `

${comment.userName} on ${formattedDate}: ${comment.userComment}

`; - commentsContainer.appendChild(commentElement); - displayedComments++; + commentsContainer.appendChild(commentElement); + displayedComments++; + } + index++; + }); + } + + document.getElementById("prev-page").disabled = currentPage <= 1; + document.getElementById("next-page").disabled = currentPage >= totalPages; + } + catch (error) { + console.error("Error fetching user list: ", error); + if (error.code === 'resource-exhausted') { + const noUserSelected = document.getElementById('comments-section'); + if (noUserSelected) { + noUserSelected.innerHTML = "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!"; + noUserSelected.style.height = '350px'; + noUserSelected.style.textAlign = 'center'; + noUserSelected.style.maxWidth = '350px'; } - index++; - }); + hideSpinner(); + } } - - document.getElementById("prev-page").disabled = currentPage <= 1; - document.getElementById("next-page").disabled = currentPage >= totalPages; } function formatCommentDate(commentDate) { diff --git a/MovieVerse-Mobile/app/js/company-details.js b/MovieVerse-Mobile/app/js/company-details.js index 06fddc39..ac8ff7c1 100644 --- a/MovieVerse-Mobile/app/js/company-details.js +++ b/MovieVerse-Mobile/app/js/company-details.js @@ -59,7 +59,7 @@ async function fetchGenreMap() { localStorage.setItem('genreMap', JSON.stringify(genreMap)); } catch (error) { - console.error('Error fetching genre map:', error); + console.log('Error fetching genre map:', error); } } @@ -91,7 +91,7 @@ async function rotateUserStats() { { label: "Favorite Movies", getValue: () => { - const favoritedMovies = JSON.parse(localStorage.getItem('favoritesMovies')) || []; + const favoritedMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; return favoritedMovies.length; } }, @@ -99,8 +99,37 @@ async function rotateUserStats() { label: "Favorite Genre", getValue: () => { const mostCommonGenreCode = getMostCommonGenre(); - const genreMap = JSON.parse(localStorage.getItem('genreMap')) || {}; - return genreMap[mostCommonGenreCode] || 'Not Available'; + const genreMapString = localStorage.getItem('genreMap'); + if (!genreMapString) { + 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'; + } + + let genreObject; + if (Array.isArray(genreMap)) { + genreObject = genreMap.reduce((acc, genre) => { + acc[genre.id] = genre.name; + return acc; + }, {}); + } + 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'; + } + + return genreObject[mostCommonGenreCode] || 'Not Available'; } }, { label: "Watchlists Created", getValue: () => localStorage.getItem('watchlistsCreated') || 0 }, @@ -368,8 +397,8 @@ async function fetchCompanyDetails(companyId) { try { const response = await fetch(url); const company = await response.json(); - const logoImg = document.getElementById('company-logo'); + if (company.logo_path) { logoImg.src = `https://image.tmdb.org/t/p/w500${company.logo_path}`; } @@ -382,9 +411,9 @@ async function fetchCompanyDetails(companyId) { const fullCountryName = twoLetterCountryCodes.find(country => country.code === company.origin_country)?.name; - document.getElementById('company-name').textContent = company.name || 'Name Not Available'; - document.getElementById('company-headquarters').textContent = company.headquarters || 'Headquarters Not Available'; - document.getElementById('company-country').textContent = fullCountryName || 'Country Not Available'; + document.getElementById('company-name').textContent = company.name || 'Information Unavailable'; + document.getElementById('company-headquarters').textContent = company.headquarters || 'Information Unavailable'; + document.getElementById('company-country').textContent = fullCountryName || 'Information Unavailable'; document.title = `${company.name} - Company Details`; const homepage = company.homepage || '#'; @@ -394,14 +423,14 @@ async function fetchCompanyDetails(companyId) { companyWebsite.textContent = homepage; } else { - companyWebsite.textContent = 'Website Not Available'; + companyWebsite.textContent = 'Information Unavailable'; } updateBrowserURL(company.name); hideSpinner(); } catch (error) { - console.error('Error fetching company details:', error); + console.log('Error fetching company details:', error); const companyDetailsContainer = document.getElementById('company-details-container'); companyDetailsContainer.innerHTML = `
@@ -424,7 +453,7 @@ async function fetchCompanyMovies(companyId) { displayCompanyMovies(data.results); } catch (error) { - console.error('Error fetching movies:', error); + console.log('Error fetching movies:', error); } } @@ -447,7 +476,7 @@ async function showMovieOfTheDay() { } } catch (error) { - console.error('Error fetching movie:', error); + console.log('Error fetching movie:', error); fallbackMovieSelection(); } } @@ -764,7 +793,7 @@ function displayCompanyMovies(movies) { movieLink.style.cursor = 'pointer'; movieLink.style.textDecoration = 'underline'; movieLink.addEventListener('mouseenter', () => { - movieLink.style.color = '#f509d9'; + movieLink.style.color = '#ff8623'; }); movieLink.addEventListener('mouseleave', () => { movieLink.style.color = 'white'; @@ -792,4 +821,4 @@ function updateBrowserURL(title) { function createNameSlug(title) { return title.toLowerCase().replace(/ /g, '-').replace(/[^\w-]/g, ''); -} \ No newline at end of file +} diff --git a/MovieVerse-Mobile/app/js/create-account.js b/MovieVerse-Mobile/app/js/create-account.js index 7cbb1764..bc5c32ac 100644 --- a/MovieVerse-Mobile/app/js/create-account.js +++ b/MovieVerse-Mobile/app/js/create-account.js @@ -1,5 +1,5 @@ import { initializeApp } from "https://www.gstatic.com/firebasejs/10.8.0/firebase-app.js"; -import { getFirestore, collection, addDoc, getDocs, query, where } from "https://www.gstatic.com/firebasejs/10.8.0/firebase-firestore.js"; +import { getFirestore, collection, addDoc, getDocs, query, where, doc, setDoc } from "https://www.gstatic.com/firebasejs/10.8.0/firebase-firestore.js"; function isValidPassword(password) { const minLength = 8; @@ -59,44 +59,73 @@ const app = initializeApp(firebaseConfig); const db = getFirestore(app); document.getElementById('createAccountForm').addEventListener('submit', async (e) => { - e.preventDefault(); - const email = document.getElementById('newEmail').value; - const password = document.getElementById('newPassword').value; - const confirmPassword = document.getElementById('confirmPassword').value; - - if (!isValidPassword(password)) { - alert('Password does not meet the security requirements.\n\n' + - 'Your password must include:\n' + - '- At least 8 characters\n' + - '- At least one uppercase letter\n' + - '- At least one lowercase letter\n' + - '- At least one number\n' + - '- At least one special character (e.g., !@#$%^&*)'); - return; - } - - if (password !== confirmPassword) { - alert('Passwords do not match.'); - return; - } - - const exists = await accountExists(email); - if (exists) { - alert('An account with this email already exists.'); - return; - } - try { - await addDoc(collection(db, "MovieVerseUsers"), { - email: email, - password: password - }); - alert('Account created successfully! Now please sign in on the sign in page to proceed.'); - window.location.href = 'sign-in.html'; + e.preventDefault(); + const email = document.getElementById('newEmail').value; + const password = document.getElementById('newPassword').value; + const confirmPassword = document.getElementById('confirmPassword').value; + + if (!isValidPassword(password)) { + alert('Password does not meet the security requirements.\n\n' + + 'Your password must include:\n' + + '- At least 8 characters\n' + + '- At least one uppercase letter\n' + + '- At least one lowercase letter\n' + + '- At least one number\n' + + '- At least one special character (e.g., !@#$%^&*)'); + return; + } + + if (password !== confirmPassword) { + alert('Passwords do not match.'); + return; + } + + const exists = await accountExists(email); + if (exists) { + alert('An account with this email already exists.'); + return; + } + + try { + await addDoc(collection(db, "MovieVerseUsers"), { + email: email, + password: password + }); + + const profileRef = doc(db, 'profiles', email); // Using email as document ID for simplicity + await setDoc(profileRef, { + username: 'N/A', + dob: 'N/A', + bio: 'N/A', + favoriteGenres: ['N/A'], + location: 'N/A', + favoriteMovie: 'N/A', + hobbies: ['N/A'], + favoriteActor: 'N/A', + favoriteDirector: 'N/A', + personalQuote: 'N/A', + profileImage: '../../images/user-default.png' + }); + + alert('Account created successfully! Now please sign in on the sign in page to proceed.'); + window.location.href = 'sign-in.html'; + } + catch (error) { + console.log("Error creating account: ", error); + alert('Failed to create account. Please try again later.'); + } } catch (error) { - console.error("Error creating account: ", error); - alert('Failed to create account. Please try again later.'); + console.error("Error fetching user list: ", error); + if (error.code === 'resource-exhausted') { + const noUserSelected = document.getElementById('account-creation-form-container'); + if (noUserSelected) { + noUserSelected.innerHTML = "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!"; + noUserSelected.style.height = '350px'; + } + hideSpinner(); + } } }); diff --git a/MovieVerse-Mobile/app/js/director-details.js b/MovieVerse-Mobile/app/js/director-details.js index ea7ff782..3803dcf4 100644 --- a/MovieVerse-Mobile/app/js/director-details.js +++ b/MovieVerse-Mobile/app/js/director-details.js @@ -125,12 +125,12 @@ async function fetchDirectorDetails(directorId) {

Director details not found - try again with a different director.

`; - console.error('Error fetching director details:', error); + console.log('Error fetching director details:', error); hideSpinner(); } } -function populateDirectorDetails(director, credits) { +async function populateDirectorDetails(director, credits) { const directorImage = document.getElementById('director-image'); const directorName = document.getElementById('director-name'); const directorDescription = document.getElementById('director-description'); @@ -164,34 +164,185 @@ function populateDirectorDetails(director, credits) { } directorDescription.innerHTML = ` -

Biography: ${director.biography || 'N/A'}

-

Date of Birth: ${director.birthday || 'N/A'}

-

Date of Death: ${director.deathday || 'N/A'}

+

Biography: ${director.biography || 'Information Unavailable'}

+

Also Known As: ${director.also_known_as.join(', ') || 'Information Unavailable'}

+

Date of Birth: ${director.birthday || 'Information Unavailable'}

+

Date of Death: ${director.deathday || 'Information Unavailable'}

Age: ${ageOrStatus}

-

Place of Birth: ${director.place_of_birth || 'N/A'}

+

Place of Birth: ${director.place_of_birth || 'Information Unavailable'}

Known For: Directing

`; const filmographyHeading = document.createElement('p'); filmographyHeading.innerHTML = 'Filmography: '; directorDescription.appendChild(filmographyHeading); + const movieList = document.createElement('div'); movieList.classList.add('movie-list'); - credits.crew.forEach(movie => { - if (movie.job === "Director") { - const movieLink = document.createElement('span'); - movieLink.textContent = movie.title; - movieLink.classList.add('movie-link'); - movieLink.addEventListener('click', () => { - localStorage.setItem('selectedMovieId', movie.id); - window.location.href = 'movie-details.html'; - }); - movieList.appendChild(movieLink); + + const directedMovies = credits.crew.filter(movie => movie.job === "Director"); + + directedMovies.forEach((movie, index) => { + const movieLink = document.createElement('span'); + movieLink.textContent = movie.title; + movieLink.classList.add('movie-link'); + movieLink.addEventListener('click', () => { + localStorage.setItem('selectedMovieId', movie.id); + window.location.href = 'movie-details.html'; + }); + movieList.appendChild(movieLink); + + if (index < directedMovies.length - 1) { movieList.appendChild(document.createTextNode(', ')); } }); + filmographyHeading.appendChild(movieList); + const mediaUrl = `https://${getMovieVerseData()}/3/person/${director.id}/images?${generateMovieNames()}${getMovieCode()}`; + const mediaResponse = await fetch(mediaUrl); + const mediaData = await mediaResponse.json(); + const images = mediaData.profiles; + + const detailsContainer = document.getElementById('director-description'); + + let mediaContainer = document.getElementById('media-container'); + if (!mediaContainer) { + mediaContainer = document.createElement('div'); + mediaContainer.id = 'media-container'; + mediaContainer.style = ` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + width: 450px; + margin: 20px auto; + overflow: hidden; + max-width: 100%; + box-sizing: border-box; + `; + detailsContainer.appendChild(mediaContainer); + } + + let mediaTitle = document.getElementById('media-title'); + if (!mediaTitle) { + mediaTitle = document.createElement('p'); + mediaTitle.id = 'media-title'; + mediaTitle.textContent = 'Media:'; + mediaTitle.style = ` + font-weight: bold; + align-self: center; + margin-bottom: 5px; + `; + } + + detailsContainer.appendChild(mediaTitle); + detailsContainer.appendChild(mediaContainer); + + let imageElement = document.getElementById('series-media-image'); + if (!imageElement) { + imageElement = document.createElement('img'); + imageElement.id = 'series-media-image'; + imageElement.style = ` + max-width: 100%; + max-height: 210px; + transition: opacity 0.5s ease-in-out; + opacity: 1; + border-radius: 16px; + cursor: pointer; + `; + mediaContainer.appendChild(imageElement); + } + + if (images.length > 0) { + imageElement.src = `https://image.tmdb.org/t/p/w1280${images[0].file_path}`; + } + + if (images.length === 0) { + mediaContainer.innerHTML = '

No media available

'; + } + + imageElement.addEventListener('click', function() { + const imageUrl = this.src; + const modalHtml = ` +
+ + × +
+ `; + document.body.insertAdjacentHTML('beforeend', modalHtml); + const modal = document.getElementById('image-modal'); + const closeModalBtn = document.getElementById('removeBtn'); + + closeModalBtn.onclick = function() { + modal.remove(); + }; + + modal.addEventListener('click', function(event) { + if (event.target === this) { + this.remove(); + } + }); + }); + + let prevButton = document.getElementById('prev-media-button'); + let nextButton = document.getElementById('next-media-button'); + if (!prevButton || !nextButton) { + prevButton = document.createElement('button'); + nextButton = document.createElement('button'); + prevButton.id = 'prev-media-button'; + nextButton.id = 'next-media-button'; + prevButton.innerHTML = ''; + nextButton.innerHTML = ''; + + [prevButton, nextButton].forEach(button => { + button.style = ` + position: absolute; + top: 50%; + transform: translateY(-50%); + background-color: #7378c5; + color: white; + border-radius: 8px; + height: 30px; + width: 30px; + border: none; + cursor: pointer; + `; + button.onmouseover = () => button.style.backgroundColor = '#ff8623'; + button.onmouseout = () => button.style.backgroundColor = '#7378c5'; + }); + + prevButton.style.left = '0'; + nextButton.style.right = '0'; + + mediaContainer.appendChild(prevButton); + mediaContainer.appendChild(nextButton); + } + + let currentIndex = 0; + prevButton.onclick = () => navigateMedia(images, imageElement, -1); + nextButton.onclick = () => navigateMedia(images, imageElement, 1); + + function navigateMedia(images, imgElement, direction) { + currentIndex += direction; + if (currentIndex < 0) { + currentIndex = images.length - 1; + } + else if (currentIndex >= images.length) { + currentIndex = 0; + } + imgElement.style.opacity = '0'; + setTimeout(() => { + imgElement.src = `https://image.tmdb.org/t/p/w1280${images[currentIndex].file_path}`; + imgElement.style.opacity = '1'; + }, 420); + } + + if (window.innerWidth <= 767) { + mediaContainer.style.width = 'calc(100% - 40px)'; + } + applySettings(); } @@ -221,7 +372,7 @@ async function fetchGenreMap() { localStorage.setItem('genreMap', JSON.stringify(genreMap)); } catch (error) { - console.error('Error fetching genre map:', error); + console.log('Error fetching genre map:', error); } } @@ -253,7 +404,7 @@ async function rotateUserStats() { { label: "Favorite Movies", getValue: () => { - const favoritedMovies = JSON.parse(localStorage.getItem('favoritesMovies')) || []; + const favoritedMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; return favoritedMovies.length; } }, @@ -261,8 +412,37 @@ async function rotateUserStats() { label: "Favorite Genre", getValue: () => { const mostCommonGenreCode = getMostCommonGenre(); - const genreMap = JSON.parse(localStorage.getItem('genreMap')) || {}; - return genreMap[mostCommonGenreCode] || 'Not Available'; + const genreMapString = localStorage.getItem('genreMap'); + if (!genreMapString) { + 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'; + } + + let genreObject; + if (Array.isArray(genreMap)) { + genreObject = genreMap.reduce((acc, genre) => { + acc[genre.id] = genre.name; + return acc; + }, {}); + } + 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'; + } + + return genreObject[mostCommonGenreCode] || 'Not Available'; } }, { label: "Watchlists Created", getValue: () => localStorage.getItem('watchlistsCreated') || 0 }, diff --git a/MovieVerse-Mobile/app/js/favorites.js b/MovieVerse-Mobile/app/js/favorites.js index 794a79cc..55518565 100644 --- a/MovieVerse-Mobile/app/js/favorites.js +++ b/MovieVerse-Mobile/app/js/favorites.js @@ -109,7 +109,7 @@ async function fetchGenreMap() { localStorage.setItem('genreMap', JSON.stringify(genreMap)); } catch (error) { - console.error('Error fetching genre map:', error); + console.log('Error fetching genre map:', error); } } @@ -141,7 +141,7 @@ async function rotateUserStats() { { label: "Favorite Movies", getValue: () => { - const favoritedMovies = JSON.parse(localStorage.getItem('favoritesMovies')) || []; + const favoritedMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; return favoritedMovies.length; } }, @@ -149,8 +149,37 @@ async function rotateUserStats() { label: "Favorite Genre", getValue: () => { const mostCommonGenreCode = getMostCommonGenre(); - const genreMap = JSON.parse(localStorage.getItem('genreMap')) || {}; - return genreMap[mostCommonGenreCode] || 'Not Available'; + const genreMapString = localStorage.getItem('genreMap'); + if (!genreMapString) { + 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'; + } + + let genreObject; + if (Array.isArray(genreMap)) { + genreObject = genreMap.reduce((acc, genre) => { + acc[genre.id] = genre.name; + return acc; + }, {}); + } + 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'; + } + + return genreObject[mostCommonGenreCode] || 'Not Available'; } }, { label: "Watchlists Created", getValue: () => localStorage.getItem('watchlistsCreated') || 0 }, @@ -348,170 +377,259 @@ async function getMovieTitle(movieId) { } async function populateCreateModalWithFavorites() { - let currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser') || ''; + try { + let currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser') || ''; + + if (!currentUserEmail) { + const moviesFavorited = JSON.parse(localStorage.getItem('moviesFavorited')) || []; + const favoritesTVSeries = JSON.parse(localStorage.getItem('favoritesTVSeries')) || []; + + let container = document.getElementById('favorites-container'); + if (!container) { + container = document.createElement('div'); + container.id = 'favorites-container'; + document.getElementById('create-watchlist-form').insertBefore(container, document.querySelector('button[type="submit"]')); + } + else { + container.innerHTML = ''; + } + + let moviesLabel = document.createElement('label'); + moviesLabel.textContent = 'Select favorite movies to include in watchlist:'; + container.appendChild(moviesLabel); + + let moviesContainer = document.createElement('div'); + moviesContainer.id = 'movies-container'; + moviesContainer.style.marginTop = '-20px'; + container.appendChild(moviesContainer); + + if (moviesFavorited.length === 0) { + moviesContainer.innerHTML = '

No Favorite Movies Added Yet.

'; + } + else { + for (const movieId of moviesFavorited) { + const movieTitle = await getMovieTitle(movieId); + appendCheckbox(moviesContainer, movieId, movieTitle, 'favoritedMovies'); + } + } + + let tvSeriesLabel = document.createElement('label'); + tvSeriesLabel.textContent = 'Select favorite TV series to include in watchlist:'; + container.appendChild(tvSeriesLabel); + + let tvSeriesContainer = document.createElement('div'); + tvSeriesContainer.id = 'tvseries-container'; + tvSeriesContainer.style.marginTop = '-20px'; + container.appendChild(tvSeriesContainer); + + if (favoritesTVSeries.length === 0) { + tvSeriesContainer.innerHTML = '

No Favorite TV Series Added Yet.

'; + } + else { + for (const seriesId of favoritesTVSeries) { + const seriesTitle = await getTVSeriesTitle(seriesId); + appendCheckbox(tvSeriesContainer, seriesId, seriesTitle, 'favoritedTVSeries'); + } + } + return; + } + + const usersRef = query(collection(db, "MovieVerseUsers"), where("email", "==", currentUserEmail)); + const userSnapshot = await getDocs(usersRef); - if (!currentUserEmail) { - const favoritesMovies = JSON.parse(localStorage.getItem('favoritesMovies')) || []; - const favoritesTVSeries = JSON.parse(localStorage.getItem('favoritesTVSeries')) || []; + const createForm = document.getElementById('create-watchlist-form'); let container = document.getElementById('favorites-container'); if (!container) { container = document.createElement('div'); container.id = 'favorites-container'; - document.getElementById('create-watchlist-form').insertBefore(container, document.querySelector('button[type="submit"]')); + createForm.insertBefore(container, createForm.querySelector('button[type="submit"]')); } else { container.innerHTML = ''; } - let moviesLabel = document.createElement('label'); - moviesLabel.textContent = 'Select favorite movies to include in watchlist:'; - container.appendChild(moviesLabel); + if (!userSnapshot.empty) { + const userData = userSnapshot.docs[0].data(); + const moviesFavorited = userData.favoritesMovies || []; + const favoritesTVSeries = userData.favoritesTVSeries || []; - let moviesContainer = document.createElement('div'); - moviesContainer.id = 'movies-container'; - moviesContainer.style.marginTop = '-20px'; - container.appendChild(moviesContainer); + let moviesLabel = document.createElement('label'); + moviesLabel.textContent = 'Select favorite movies to include in watchlist:'; + container.appendChild(moviesLabel); - if (favoritesMovies.length === 0) { - moviesContainer.innerHTML = '

No Favorite Movies Added Yet.

'; - } - else { - for (const movieId of favoritesMovies) { - const movieTitle = await getMovieTitle(movieId); - appendCheckbox(moviesContainer, movieId, movieTitle, 'favoritedMovies'); + let moviesContainer = document.createElement('div'); + moviesContainer.id = 'movies-container'; + moviesContainer.style.marginTop = '-20px'; + container.appendChild(moviesContainer); + + if (moviesFavorited.length === 0) { + moviesContainer.innerHTML = '

No Favorite Movies Added Yet.

'; + } + else { + for (const movieId of moviesFavorited) { + const movieTitle = await getMovieTitle(movieId); + appendCheckbox(moviesContainer, movieId, movieTitle, 'favoritedMovies'); + } } - } - let tvSeriesLabel = document.createElement('label'); - tvSeriesLabel.textContent = 'Select favorite TV series to include in watchlist:'; - container.appendChild(tvSeriesLabel); + let tvSeriesLabel = document.createElement('label'); + tvSeriesLabel.textContent = 'Select favorite TV series to include in watchlist:'; + container.appendChild(tvSeriesLabel); - let tvSeriesContainer = document.createElement('div'); - tvSeriesContainer.id = 'tvseries-container'; - tvSeriesContainer.style.marginTop = '-20px'; - container.appendChild(tvSeriesContainer); + let tvSeriesContainer = document.createElement('div'); + tvSeriesContainer.id = 'tvseries-container'; + tvSeriesContainer.style.marginTop = '-20px'; + container.appendChild(tvSeriesContainer); - if (favoritesTVSeries.length === 0) { - tvSeriesContainer.innerHTML = '

No Favorite TV Series Added Yet.

'; + if (favoritesTVSeries.length === 0) { + tvSeriesContainer.innerHTML = '

No Favorite TV Series Added Yet.

'; + } + else { + for (const seriesId of favoritesTVSeries) { + const seriesTitle = await getTVSeriesTitle(seriesId); + appendCheckbox(tvSeriesContainer, seriesId, seriesTitle, 'favoritedTVSeries'); + } + } } else { - for (const seriesId of favoritesTVSeries) { - const seriesTitle = await getTVSeriesTitle(seriesId); - appendCheckbox(tvSeriesContainer, seriesId, seriesTitle, 'favoritedTVSeries'); - } + container.innerHTML = '

No favorites found. Please add some favorites first.

'; } - return; - } - - const usersRef = query(collection(db, "MovieVerseUsers"), where("email", "==", currentUserEmail)); - const userSnapshot = await getDocs(usersRef); - - const createForm = document.getElementById('create-watchlist-form'); - - let container = document.getElementById('favorites-container'); - if (!container) { - container = document.createElement('div'); - container.id = 'favorites-container'; - createForm.insertBefore(container, createForm.querySelector('button[type="submit"]')); - } - else { - container.innerHTML = ''; } + catch (error) { + if (error.code === 'resource-exhausted') { + console.log('Firebase quota exceeded. Using localStorage for favorites.'); + const moviesFavorited = JSON.parse(localStorage.getItem('moviesFavorited')) || []; + const favoritesTVSeries = JSON.parse(localStorage.getItem('favoritesTVSeries')) || []; + + let container = document.getElementById('favorites-container'); + if (!container) { + container = document.createElement('div'); + container.id = 'favorites-container'; + document.getElementById('create-watchlist-form').insertBefore(container, document.querySelector('button[type="submit"]')); + } + else { + container.innerHTML = ''; + } - if (!userSnapshot.empty) { - const userData = userSnapshot.docs[0].data(); - const favoritesMovies = userData.favoritesMovies || []; - const favoritesTVSeries = userData.favoritesTVSeries || []; - - let moviesLabel = document.createElement('label'); - moviesLabel.textContent = 'Select favorite movies to include in watchlist:'; - container.appendChild(moviesLabel); + let moviesLabel = document.createElement('label'); + moviesLabel.textContent = 'Select favorite movies to include in watchlist:'; + container.appendChild(moviesLabel); - let moviesContainer = document.createElement('div'); - moviesContainer.id = 'movies-container'; - moviesContainer.style.marginTop = '-20px'; - container.appendChild(moviesContainer); + let moviesContainer = document.createElement('div'); + moviesContainer.id = 'movies-container'; + moviesContainer.style.marginTop = '-20px'; + container.appendChild(moviesContainer); - if (favoritesMovies.length === 0) { - moviesContainer.innerHTML = '

No Favorite Movies Added Yet.

'; - } - else { - for (const movieId of favoritesMovies) { - const movieTitle = await getMovieTitle(movieId); - appendCheckbox(moviesContainer, movieId, movieTitle, 'favoritedMovies'); + if (moviesFavorited.length === 0) { + moviesContainer.innerHTML = '

No Favorite Movies Added Yet.

'; + } + else { + for (const movieId of moviesFavorited) { + const movieTitle = await getMovieTitle(movieId); + appendCheckbox(moviesContainer, movieId, movieTitle, 'favoritedMovies'); + } } - } - let tvSeriesLabel = document.createElement('label'); - tvSeriesLabel.textContent = 'Select favorite TV series to include in watchlist:'; - container.appendChild(tvSeriesLabel); + let tvSeriesLabel = document.createElement('label'); + tvSeriesLabel.textContent = 'Select favorite TV series to include in watchlist:'; + container.appendChild(tvSeriesLabel); - let tvSeriesContainer = document.createElement('div'); - tvSeriesContainer.id = 'tvseries-container'; - tvSeriesContainer.style.marginTop = '-20px'; - container.appendChild(tvSeriesContainer); + let tvSeriesContainer = document.createElement('div'); + tvSeriesContainer.id = 'tvseries-container'; + tvSeriesContainer.style.marginTop = '-20px'; + container.appendChild(tvSeriesContainer); - if (favoritesTVSeries.length === 0) { - tvSeriesContainer.innerHTML = '

No Favorite TV Series Added Yet.

'; - } - else { - for (const seriesId of favoritesTVSeries) { - const seriesTitle = await getTVSeriesTitle(seriesId); - appendCheckbox(tvSeriesContainer, seriesId, seriesTitle, 'favoritedTVSeries'); + if (favoritesTVSeries.length === 0) { + tvSeriesContainer.innerHTML = '

No Favorite TV Series Added Yet.

'; + } + else { + for (const seriesId of favoritesTVSeries) { + const seriesTitle = await getTVSeriesTitle(seriesId); + appendCheckbox(tvSeriesContainer, seriesId, seriesTitle, 'favoritedTVSeries'); + } } } } - else { - container.innerHTML = '

No favorites found. Please add some favorites first.

'; - } + document.addEventListener('keydown', function(event) { + if (event.key === "Escape") { + closeModal('create-watchlist-modal'); + } + }); } document.getElementById('create-watchlist-form').addEventListener('submit', async function(e) { - showSpinner(); - e.preventDefault(); - - const name = document.getElementById('new-watchlist-name').value; - const description = document.getElementById('new-watchlist-description').value; - const selectedMovies = Array.from(document.querySelectorAll('#movies-container input:checked')).map(checkbox => checkbox.value); - const selectedTVSeries = Array.from(document.querySelectorAll('#tvseries-container input:checked')).map(checkbox => checkbox.value); - const currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); - const q = query(collection(db, "watchlists"), where("userEmail", "==", currentUserEmail)); - const querySnapshot = await getDocs(q); - let maxOrder = querySnapshot.docs.reduce((max, docSnapshot) => Math.max(max, docSnapshot.data().order || 0), 0); + try { + showSpinner(); + e.preventDefault(); + + const name = document.getElementById('new-watchlist-name').value; + const description = document.getElementById('new-watchlist-description').value; + const selectedMovies = Array.from(document.querySelectorAll('#movies-container input:checked')).map(checkbox => checkbox.value); + const selectedTVSeries = Array.from(document.querySelectorAll('#tvseries-container input:checked')).map(checkbox => checkbox.value); + const currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); + const q = query(collection(db, "watchlists"), where("userEmail", "==", currentUserEmail)); + const querySnapshot = await getDocs(q); + let maxOrder = querySnapshot.docs.reduce((max, docSnapshot) => Math.max(max, docSnapshot.data().order || 0), 0); + + if (currentUserEmail) { + const newWatchlistRef = doc(collection(db, 'watchlists')); + await setDoc(newWatchlistRef, { + userEmail: currentUserEmail, + name, + description, + movies: selectedMovies, + tvSeries: selectedTVSeries, + pinned: false, + createdAt: new Date().toISOString(), + order: maxOrder + 1, + }); + } + else { + const localWatchlists = JSON.parse(localStorage.getItem('localWatchlists')) || []; + localWatchlists.push({ + id: `local-${new Date().getTime()}`, + userEmail: "", + name, + description, + movies: selectedMovies, + tvSeries: selectedTVSeries, + pinned: false, + createdAt: new Date().toISOString() + }); + localStorage.setItem('localWatchlists', JSON.stringify(localWatchlists)); + } - if (currentUserEmail) { - const newWatchlistRef = doc(collection(db, 'watchlists')); - await setDoc(newWatchlistRef, { - userEmail: currentUserEmail, - name, - description, - movies: selectedMovies, - tvSeries: selectedTVSeries, - pinned: false, - createdAt: new Date().toISOString(), - order: maxOrder + 1, - }); + closeModal('create-watchlist-modal'); + loadWatchLists(); + hideSpinner(); + window.location.reload(); } - else { - const localWatchlists = JSON.parse(localStorage.getItem('localWatchlists')) || []; - localWatchlists.push({ - id: `local-${new Date().getTime()}`, - userEmail: "", - name, - description, - movies: selectedMovies, - tvSeries: selectedTVSeries, - pinned: false, - createdAt: new Date().toISOString() - }); - localStorage.setItem('localWatchlists', JSON.stringify(localWatchlists)); + catch (error) { + if (error.code === 'resource-exhausted') { + console.log('Firebase quota exceeded. Using localStorage for watchlists.'); + const name = document.getElementById('new-watchlist-name').value; + const description = document.getElementById('new-watchlist-description').value; + const selectedMovies = Array.from(document.querySelectorAll('#movies-container input:checked')).map(checkbox => checkbox.value); + const selectedTVSeries = Array.from(document.querySelectorAll('#tvseries-container input:checked')).map(checkbox => checkbox.value); + const localWatchlists = JSON.parse(localStorage.getItem('localWatchlists')) || []; + localWatchlists.push({ + id: `local-${new Date().getTime()}`, + userEmail: "", + name, + description, + movies: selectedMovies, + tvSeries: selectedTVSeries, + pinned: false, + createdAt: new Date().toISOString() + }); + localStorage.setItem('localWatchlists', JSON.stringify(localWatchlists)); + closeModal('create-watchlist-modal'); + loadWatchLists(); + hideSpinner(); + window.location.reload(); + } } - - closeModal('create-watchlist-modal'); - loadWatchLists(); - hideSpinner(); - window.location.reload(); }); async function getTVSeriesTitle(seriesId) { @@ -552,7 +670,6 @@ function appendCheckbox(container, id, title, name, isChecked = false) { container.appendChild(item); } - document.getElementById('create-watchlist-btn').addEventListener('click', function() { document.getElementById('create-watchlist-form').reset(); populateCreateModalWithFavorites(); @@ -570,279 +687,528 @@ document.getElementById('edit-watchlist-btn').addEventListener('click', async fu }); async function populateEditModal() { - let currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); + try { + let currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); - let watchlists = []; - let favoritesMovies = []; - let favoritesTVSeries = []; + let watchlists = []; + let moviesFavorited = []; + let favoritesTVSeries = []; - if (currentUserEmail) { - const qWatchlists = query(collection(db, "watchlists"), where("userEmail", "==", currentUserEmail)); - const qUsers = query(collection(db, "MovieVerseUsers"), where("email", "==", currentUserEmail)); + if (currentUserEmail) { + const qWatchlists = query(collection(db, "watchlists"), where("userEmail", "==", currentUserEmail)); + const qUsers = query(collection(db, "MovieVerseUsers"), where("email", "==", currentUserEmail)); - const [watchlistsSnapshot, usersSnapshot] = await Promise.all([ - getDocs(qWatchlists), - getDocs(qUsers) - ]); + const [watchlistsSnapshot, usersSnapshot] = await Promise.all([ + getDocs(qWatchlists), + getDocs(qUsers) + ]); - watchlists = watchlistsSnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); + watchlists = watchlistsSnapshot.docs.map(doc => ({id: doc.id, ...doc.data()})); - if (!usersSnapshot.empty) { - const userData = usersSnapshot.docs[0].data(); - favoritesMovies = userData.favoritesMovies || []; - favoritesTVSeries = userData.favoritesTVSeries || []; + if (!usersSnapshot.empty) { + const userData = usersSnapshot.docs[0].data(); + moviesFavorited = userData.favoritesMovies || []; + favoritesTVSeries = userData.favoritesTVSeries || []; + } + } + else { + watchlists = JSON.parse(localStorage.getItem('localWatchlists')) || []; + moviesFavorited = JSON.parse(localStorage.getItem('moviesFavorited')) || []; + favoritesTVSeries = JSON.parse(localStorage.getItem('favoritesTVSeries')) || []; } - } - else { - watchlists = JSON.parse(localStorage.getItem('localWatchlists')) || []; - favoritesMovies = JSON.parse(localStorage.getItem('favoritesMovies')) || []; - favoritesTVSeries = JSON.parse(localStorage.getItem('favoritesTVSeries')) || []; - } - const editForm = document.getElementById('edit-watchlist-form'); - editForm.innerHTML = ''; + const editForm = document.getElementById('edit-watchlist-form'); + editForm.innerHTML = ''; - if (watchlists.length === 0) { - const noWatchlistMsg = document.createElement('div'); - noWatchlistMsg.textContent = 'No Watchlists Available for Edit'; - noWatchlistMsg.style.textAlign = 'center'; - noWatchlistMsg.style.marginTop = '30px'; - noWatchlistMsg.style.color = 'white'; - editForm.appendChild(noWatchlistMsg); - return; - } + if (watchlists.length === 0) { + const noWatchlistMsg = document.createElement('div'); + noWatchlistMsg.textContent = 'No Watch Lists Available for Edit'; + noWatchlistMsg.style.textAlign = 'center'; + noWatchlistMsg.style.marginTop = '30px'; + noWatchlistMsg.style.color = 'white'; + editForm.appendChild(noWatchlistMsg); + return; + } - const selectLabel = document.createElement('label'); - selectLabel.textContent = 'Select A Watch List:'; - selectLabel.setAttribute("for", "watchlist-select"); - editForm.appendChild(selectLabel); - - const select = document.createElement('select'); - select.id = 'watchlist-select'; - select.style.font = 'inherit'; - watchlists.forEach((watchlist) => { - const option = document.createElement('option'); - option.value = watchlist.id; - option.textContent = watchlist.name; - select.appendChild(option); - }); + const selectLabel = document.createElement('label'); + selectLabel.textContent = 'Select A Watch List:'; + selectLabel.setAttribute("for", "watchlist-select"); + editForm.appendChild(selectLabel); + + const select = document.createElement('select'); + select.id = 'watchlist-select'; + select.style.font = 'inherit'; + watchlists.forEach((watchlist) => { + const option = document.createElement('option'); + option.value = watchlist.id; + option.textContent = watchlist.name; + select.appendChild(option); + }); - const nameLabel = document.createElement('label'); - nameLabel.textContent = 'Watch List Name:'; - const nameInput = document.createElement('input'); - nameInput.type = 'text'; - nameInput.id = 'edit-watchlist-name'; - nameInput.style.font = 'inherit'; - nameInput.placeholder = 'New Watchlist Name'; - - const descLabel = document.createElement('label'); - descLabel.textContent = 'Description:'; - const descInput = document.createElement('textarea'); - descInput.id = 'edit-watchlist-description'; - descInput.style.font = 'inherit'; - descInput.placeholder = 'New Watchlist Description'; + const nameLabel = document.createElement('label'); + nameLabel.textContent = 'Watch List Name:'; + const nameInput = document.createElement('input'); + nameInput.type = 'text'; + nameInput.id = 'edit-watchlist-name'; + nameInput.style.font = 'inherit'; + nameInput.placeholder = 'New Watchlist Name'; + + const descLabel = document.createElement('label'); + descLabel.textContent = 'Description:'; + const descInput = document.createElement('textarea'); + descInput.id = 'edit-watchlist-description'; + descInput.style.font = 'inherit'; + descInput.placeholder = 'New Watchlist Description'; - const moviesContainer = document.createElement('div'); - moviesContainer.id = 'edit-movies-container'; - const moviesLabel = document.createElement('label'); - moviesLabel.textContent = 'Select favorite movies to include in watchlist:'; - editForm.appendChild(select); - editForm.appendChild(nameLabel); - editForm.appendChild(nameInput); - editForm.appendChild(descLabel); - editForm.appendChild(descInput); - editForm.appendChild(moviesLabel); - editForm.appendChild(moviesContainer); - - const tvSeriesContainer = document.createElement('div'); - tvSeriesContainer.id = 'edit-tvseries-container'; - const tvSeriesLabel = document.createElement('label'); - tvSeriesLabel.textContent = 'Select favorite TV series to include in watchlist:'; - tvSeriesLabel.style.marginTop = '20px'; - editForm.appendChild(tvSeriesLabel); - editForm.appendChild(tvSeriesContainer); - - const updateForm = async (watchlist) => { - nameInput.value = watchlist.name; - descInput.value = watchlist.description; - moviesContainer.innerHTML = ''; - tvSeriesContainer.innerHTML = ''; + const moviesContainer = document.createElement('div'); + moviesContainer.id = 'edit-movies-container'; + const moviesLabel = document.createElement('label'); + moviesLabel.textContent = 'Select favorite movies to include in watchlist:'; + editForm.appendChild(select); + editForm.appendChild(nameLabel); + editForm.appendChild(nameInput); + editForm.appendChild(descLabel); + editForm.appendChild(descInput); + editForm.appendChild(moviesLabel); + editForm.appendChild(moviesContainer); + + const tvSeriesContainer = document.createElement('div'); + tvSeriesContainer.id = 'edit-tvseries-container'; + const tvSeriesLabel = document.createElement('label'); + tvSeriesLabel.textContent = 'Select favorite TV series to include in watchlist:'; + tvSeriesLabel.style.marginTop = '20px'; + editForm.appendChild(tvSeriesLabel); + editForm.appendChild(tvSeriesContainer); - initialMoviesSelection = watchlist.movies.slice(); - initialTVSeriesSelection = watchlist.tvSeries.slice(); + const updateForm = async (watchlist) => { + nameInput.value = watchlist.name; + descInput.value = watchlist.description; + moviesContainer.innerHTML = ''; + tvSeriesContainer.innerHTML = ''; - if (!favoritesMovies || favoritesMovies.length === 0) { - moviesContainer.innerHTML = '

No Favorite Movies Added Yet.

'; - } - else { - for (const movieId of favoritesMovies) { - const movieTitle = await getMovieTitle(movieId); - const isChecked = watchlist.movies.includes(movieId); - appendCheckbox(moviesContainer, movieId, movieTitle, 'favoritedMovies', isChecked); + initialMoviesSelection = watchlist.movies.slice(); + initialTVSeriesSelection = watchlist.tvSeries.slice(); + + if (!moviesFavorited || moviesFavorited.length === 0) { + moviesContainer.innerHTML = '

No Favorite Movies Added Yet.

'; + } + else { + for (const movieId of moviesFavorited) { + const movieTitle = await getMovieTitle(movieId); + const isChecked = watchlist.movies.includes(movieId); + appendCheckbox(moviesContainer, movieId, movieTitle, 'favoritedMovies', isChecked); + } } - } - if (!favoritesTVSeries || favoritesTVSeries.length === 0) { - tvSeriesContainer.innerHTML = '

No Favorite TV Series Added Yet.

'; - } - else { - for (const seriesId of favoritesTVSeries) { - const seriesTitle = await getTVSeriesTitle(seriesId); - const isChecked = watchlist.tvSeries.includes(seriesId); - appendCheckbox(tvSeriesContainer, seriesId, seriesTitle, 'favoritedTVSeries', isChecked); + if (!favoritesTVSeries || favoritesTVSeries.length === 0) { + tvSeriesContainer.innerHTML = '

No Favorite TV Series Added Yet.

'; } - } - }; + else { + for (const seriesId of favoritesTVSeries) { + const seriesTitle = await getTVSeriesTitle(seriesId); + const isChecked = watchlist.tvSeries.includes(seriesId); + appendCheckbox(tvSeriesContainer, seriesId, seriesTitle, 'favoritedTVSeries', isChecked); + } + } + }; - select.addEventListener('change', function () { - const selectedWatchlist = watchlists.find(watchlist => watchlist.id === this.value); - updateForm(selectedWatchlist); - }); + select.addEventListener('change', function () { + const selectedWatchlist = watchlists.find(watchlist => watchlist.id === this.value); + updateForm(selectedWatchlist); + }); - selectLabel.addEventListener('click', function () { - updateForm(watchlists[select.value]); - }); + selectLabel.addEventListener('click', function () { + updateForm(watchlists[select.value]); + }); + + if (watchlists.length > 0) { + updateForm(watchlists[0]); + } - if (watchlists.length > 0) { - updateForm(watchlists[0]); + const submitButton = document.createElement('button'); + submitButton.type = 'submit'; + submitButton.textContent = 'Save Changes'; + editForm.appendChild(submitButton); + + const cancelButton = document.createElement('button'); + cancelButton.type = 'button'; + cancelButton.textContent = 'Cancel Changes'; + cancelButton.style.marginTop = '20px'; + cancelButton.onclick = () => closeModal('edit-watchlist-modal'); + editForm.appendChild(cancelButton); } + catch (error) { + if (error.code === 'resource-exhausted') { + console.log('Firebase quota exceeded. Using localStorage for watchlists.'); + let watchlists = JSON.parse(localStorage.getItem('localWatchlists')) || []; + let moviesFavorited = JSON.parse(localStorage.getItem('moviesFavorited')) || []; + let favoritesTVSeries = JSON.parse(localStorage.getItem('favoritesTVSeries')) || []; + + const editForm = document.getElementById('edit-watchlist-form'); + editForm.innerHTML = ''; + + if (watchlists.length === 0) { + const noWatchlistMsg = document.createElement('div'); + noWatchlistMsg.textContent = 'No Watch Lists Available for Edit'; + noWatchlistMsg.style.textAlign = 'center'; + noWatchlistMsg.style.marginTop = '30px'; + noWatchlistMsg.style.color = 'white'; + editForm.appendChild(noWatchlistMsg); + return; + } + + const selectLabel = document.createElement('label'); + selectLabel.textContent = 'Select A Watch List:'; + selectLabel.setAttribute("for", "watchlist-select"); + editForm.appendChild(selectLabel); + + const select = document.createElement('select'); + select.id = 'watchlist-select'; + select.style.font = 'inherit'; + watchlists.forEach((watchlist) => { + const option = document.createElement('option'); + option.value = watchlist.id; + option.textContent = watchlist.name; + select.appendChild(option); + }); + + const nameLabel = document.createElement('label'); + nameLabel.textContent = 'Watch List Name:'; + const nameInput = document.createElement('input'); + nameInput.type = 'text'; + nameInput.id = 'edit-watchlist-name'; + nameInput.style.font = 'inherit'; + nameInput.placeholder = 'New Watchlist Name'; + + const descLabel = document.createElement('label'); + descLabel.textContent = 'Description:'; + const descInput = document.createElement('textarea'); + descInput.id = 'edit-watchlist-description'; + descInput.style.font = 'inherit'; + descInput.placeholder = 'New Watchlist Description'; + + const moviesContainer = document.createElement('div'); + moviesContainer.id = 'edit-movies-container'; + const moviesLabel = document.createElement('label'); + moviesLabel.textContent = 'Select favorite movies to include in watchlist:'; + editForm.appendChild(select); + editForm.appendChild(nameLabel); + editForm.appendChild(nameInput); + editForm.appendChild(descLabel); + editForm.appendChild(descInput); + editForm.appendChild(moviesLabel); + editForm.appendChild(moviesContainer); + + const tvSeriesContainer = document.createElement('div'); + tvSeriesContainer.id = 'edit-tvseries-container'; + const tvSeriesLabel = document.createElement('label'); + tvSeriesLabel.textContent = 'Select favorite TV series to include in watchlist:'; + tvSeriesLabel.style.marginTop = '20px'; + editForm.appendChild(tvSeriesLabel); + editForm.appendChild(tvSeriesContainer); + + const updateForm = async (watchlist) => { + nameInput.value = watchlist.name; + descInput.value = watchlist.description; + moviesContainer.innerHTML = ''; + tvSeriesContainer.innerHTML = ''; + + initialMoviesSelection = watchlist.movies.slice(); + initialTVSeriesSelection = watchlist.tvSeries.slice(); + + if (!moviesFavorited || moviesFavorited.length === 0) { + moviesContainer.innerHTML = '

No Favorite Movies Added Yet.

'; + } + else { + for (const movieId of moviesFavorited) { + const movieTitle = await getMovieTitle(movieId); + const isChecked = watchlist.movies.includes(movieId); + appendCheckbox(moviesContainer, movieId, movieTitle, 'favoritedMovies', isChecked); + } + } - const submitButton = document.createElement('button'); - submitButton.type = 'submit'; - submitButton.textContent = 'Save Changes'; - editForm.appendChild(submitButton); - - const cancelButton = document.createElement('button'); - cancelButton.type = 'button'; - cancelButton.textContent = 'Cancel Changes'; - cancelButton.style.marginTop = '20px'; - cancelButton.onclick = () => closeModal('edit-watchlist-modal'); - editForm.appendChild(cancelButton); + if (!favoritesTVSeries || favoritesTVSeries.length === 0) { + tvSeriesContainer.innerHTML = '

No Favorite TV Series Added Yet.

'; + } + else { + for (const seriesId of favoritesTVSeries) { + const seriesTitle = await getTVSeriesTitle(seriesId); + const isChecked = watchlist.tvSeries.includes(seriesId); + appendCheckbox(tvSeriesContainer, seriesId, seriesTitle, 'favoritedTVSeries', isChecked); + } + } + }; + + select.addEventListener('change', function () { + const selectedWatchlist = watchlists.find(watchlist => watchlist.id === this.value); + updateForm(selectedWatchlist); + }); + + selectLabel.addEventListener('click', function () { + updateForm(watchlists[select.value]); + }); + + if (watchlists.length > 0) { + updateForm(watchlists[0]); + } + + const submitButton = document.createElement('button'); + submitButton.type = 'submit'; + submitButton.textContent = 'Save Changes'; + editForm.appendChild(submitButton); + + const cancelButton = document.createElement('button'); + cancelButton.type = 'button'; + cancelButton.textContent = 'Cancel Changes'; + cancelButton.style.marginTop = '20px'; + cancelButton.onclick = () => closeModal('edit-watchlist-modal'); + editForm.appendChild(cancelButton); + } + } + document.addEventListener('keydown', function(event) { + if (event.key === "Escape") { + closeModal('edit-watchlist-modal'); + } + }); } document.getElementById('edit-watchlist-form').addEventListener('submit', async function(e) { - showSpinner(); + try { + showSpinner(); + e.preventDefault(); - e.preventDefault(); + const currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); + const selectedOption = document.getElementById('watchlist-select'); + const watchlistId = selectedOption.value; + const newName = document.getElementById('edit-watchlist-name').value; + const newDescription = document.getElementById('edit-watchlist-description').value; - const currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); - const selectedOption = document.getElementById('watchlist-select'); - const watchlistId = selectedOption.value; - const newName = document.getElementById('edit-watchlist-name').value; - const newDescription = document.getElementById('edit-watchlist-description').value; + let selectedMovies; + let selectedTVSeries; - let selectedMovies; - let selectedTVSeries; + const currentMoviesSelection = Array.from(document.querySelectorAll('#edit-movies-container input[type="checkbox"]:checked')).map(checkbox => checkbox.value); + const currentTVSeriesSelection = Array.from(document.querySelectorAll('#edit-tvseries-container input[type="checkbox"]:checked')).map(checkbox => checkbox.value); - const currentMoviesSelection = Array.from(document.querySelectorAll('#edit-movies-container input[type="checkbox"]:checked')).map(checkbox => checkbox.value); - const currentTVSeriesSelection = Array.from(document.querySelectorAll('#edit-tvseries-container input[type="checkbox"]:checked')).map(checkbox => checkbox.value); + const moviesSelectionChanged = !(initialMoviesSelection.length === currentMoviesSelection.length && initialMoviesSelection.every(value => currentMoviesSelection.includes(value))); + const tvSeriesSelectionChanged = !(initialTVSeriesSelection.length === currentTVSeriesSelection.length && initialTVSeriesSelection.every(value => currentTVSeriesSelection.includes(value))); - const moviesSelectionChanged = !(initialMoviesSelection.length === currentMoviesSelection.length && initialMoviesSelection.every(value => currentMoviesSelection.includes(value))); - const tvSeriesSelectionChanged = !(initialTVSeriesSelection.length === currentTVSeriesSelection.length && initialTVSeriesSelection.every(value => currentTVSeriesSelection.includes(value))); + if (moviesSelectionChanged) { + selectedMovies = currentMoviesSelection; + } + else { + selectedMovies = initialMoviesSelection; + } - if (moviesSelectionChanged) { - selectedMovies = currentMoviesSelection; - } - else { - selectedMovies = initialMoviesSelection; - } + if (tvSeriesSelectionChanged) { + selectedTVSeries = currentTVSeriesSelection; + } + else { + selectedTVSeries = initialTVSeriesSelection; + } - if (tvSeriesSelectionChanged) { - selectedTVSeries = currentTVSeriesSelection; - } - else { - selectedTVSeries = initialTVSeriesSelection; - } + if (currentUserEmail) { + const q = query(collection(db, "watchlists"), where("userEmail", "==", currentUserEmail)); + const querySnapshot = await getDocs(q); - if (currentUserEmail) { - const watchlistRef = doc(db, 'watchlists', watchlistId); - await updateDoc(watchlistRef, { - name: newName, - description: newDescription, - movies: selectedMovies, - tvSeries: selectedTVSeries - }); - } - else { - let localWatchlists = JSON.parse(localStorage.getItem('localWatchlists')) || []; - let watchlistIndex = localWatchlists.findIndex(watchlist => watchlist.id === watchlistId); - if (watchlistIndex !== -1) { - localWatchlists[watchlistIndex] = { - ...localWatchlists[watchlistIndex], + const watchlistRef = doc(db, 'watchlists', watchlistId); + await updateDoc(watchlistRef, { name: newName, description: newDescription, movies: selectedMovies, tvSeries: selectedTVSeries - }; - localStorage.setItem('localWatchlists', JSON.stringify(localWatchlists)); + }); + } + else { + let localWatchlists = JSON.parse(localStorage.getItem('localWatchlists')) || []; + let watchlistIndex = localWatchlists.findIndex(watchlist => watchlist.id === watchlistId); + if (watchlistIndex !== -1) { + localWatchlists[watchlistIndex] = { + ...localWatchlists[watchlistIndex], + name: newName, + description: newDescription, + movies: selectedMovies, + tvSeries: selectedTVSeries + }; + localStorage.setItem('localWatchlists', JSON.stringify(localWatchlists)); + } } + + closeModal('edit-watchlist-modal'); + loadWatchLists(); + hideSpinner(); + window.location.reload(); } + catch (error) { + if (error.code === 'resource-exhausted') { + showSpinner(); - closeModal('edit-watchlist-modal'); - loadWatchLists(); - hideSpinner(); - window.location.reload(); + e.preventDefault(); + + const currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); + const selectedOption = document.getElementById('watchlist-select'); + const watchlistId = selectedOption.value; + const newName = document.getElementById('edit-watchlist-name').value; + const newDescription = document.getElementById('edit-watchlist-description').value; + + let selectedMovies; + let selectedTVSeries; + + const currentMoviesSelection = Array.from(document.querySelectorAll('#edit-movies-container input[type="checkbox"]:checked')).map(checkbox => checkbox.value); + const currentTVSeriesSelection = Array.from(document.querySelectorAll('#edit-tvseries-container input[type="checkbox"]:checked')).map(checkbox => checkbox.value); + + const moviesSelectionChanged = !(initialMoviesSelection.length === currentMoviesSelection.length && initialMoviesSelection.every(value => currentMoviesSelection.includes(value))); + const tvSeriesSelectionChanged = !(initialTVSeriesSelection.length === currentTVSeriesSelection.length && initialTVSeriesSelection.every(value => currentTVSeriesSelection.includes(value))); + + if (moviesSelectionChanged) { + selectedMovies = currentMoviesSelection; + } + else { + selectedMovies = initialMoviesSelection; + } + + if (tvSeriesSelectionChanged) { + selectedTVSeries = currentTVSeriesSelection; + } + else { + selectedTVSeries = initialTVSeriesSelection; + } + + let localWatchlists = JSON.parse(localStorage.getItem('localWatchlists')) || []; + let watchlistIndex = localWatchlists.findIndex(watchlist => watchlist.id === watchlistId); + if (watchlistIndex !== -1) { + localWatchlists[watchlistIndex] = { + ...localWatchlists[watchlistIndex], + name: newName, + description: newDescription, + movies: selectedMovies, + tvSeries: selectedTVSeries + }; + localStorage.setItem('localWatchlists', JSON.stringify(localWatchlists)); + } + + closeModal('edit-watchlist-modal'); + loadWatchLists(); + hideSpinner(); + window.location.reload(); + } + } }); async function populateDeleteModal() { - let currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); + try { + let currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); - const deleteForm = document.getElementById('delete-watchlist-form'); - deleteForm.innerHTML = ''; + const deleteForm = document.getElementById('delete-watchlist-form'); + deleteForm.innerHTML = ''; - let watchlists = []; + let watchlists = []; - if (currentUserEmail) { - const q = query(collection(db, "watchlists"), where("userEmail", "==", currentUserEmail)); - const querySnapshot = await getDocs(q); - watchlists = querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); - } - else { - watchlists = JSON.parse(localStorage.getItem('localWatchlists')) || []; - } + if (currentUserEmail) { + const q = query(collection(db, "watchlists"), where("userEmail", "==", currentUserEmail)); + const querySnapshot = await getDocs(q); + watchlists = querySnapshot.docs.map(doc => ({id: doc.id, ...doc.data()})); + } else { + watchlists = JSON.parse(localStorage.getItem('localWatchlists')) || []; + } - if (watchlists.length === 0) { - deleteForm.innerHTML = '

No Watchlists Available to Delete.

'; - return; + if (watchlists.length === 0) { + deleteForm.innerHTML = '

No Watchlists Available to Delete.

'; + return; + } + + const checkboxesContainer = document.createElement('div'); + checkboxesContainer.id = 'delete-watchlist-checkboxes-container'; + + watchlists.forEach(watchlist => { + appendCheckbox(checkboxesContainer, watchlist.id, watchlist.name, 'watchlistToDelete'); + }); + + deleteForm.appendChild(checkboxesContainer); + + const deleteButton = document.createElement('button'); + deleteButton.type = 'button'; + deleteButton.textContent = 'Delete Selected'; + deleteButton.onclick = deleteSelectedWatchlists; + deleteForm.appendChild(deleteButton); } + catch (error) { + if (error.code === 'resource-exhausted') { + console.log('Firebase quota exceeded. Using localStorage for watchlists.'); + let watchlists = JSON.parse(localStorage.getItem('localWatchlists')) || []; - const checkboxesContainer = document.createElement('div'); - checkboxesContainer.id = 'delete-watchlist-checkboxes-container'; + const deleteForm = document.getElementById('delete-watchlist-form'); + deleteForm.innerHTML = ''; - watchlists.forEach(watchlist => { - appendCheckbox(checkboxesContainer, watchlist.id, watchlist.name, 'watchlistToDelete'); - }); + if (watchlists.length === 0) { + deleteForm.innerHTML = '

No Watchlists Available to Delete.

'; + return; + } + + const checkboxesContainer = document.createElement('div'); + checkboxesContainer.id = 'delete-watchlist-checkboxes-container'; - deleteForm.appendChild(checkboxesContainer); + watchlists.forEach(watchlist => { + appendCheckbox(checkboxesContainer, watchlist.id, watchlist.name, 'watchlistToDelete'); + }); + + deleteForm.appendChild(checkboxesContainer); - const deleteButton = document.createElement('button'); - deleteButton.type = 'button'; - deleteButton.textContent = 'Delete Selected'; - deleteButton.onclick = deleteSelectedWatchlists; - deleteForm.appendChild(deleteButton); + const deleteButton = document.createElement('button'); + deleteButton.type = 'button'; + deleteButton.textContent = 'Delete Selected'; + deleteButton.onclick = deleteSelectedWatchlists; + deleteForm.appendChild(deleteButton); + } + } + document.addEventListener('keydown', function(event) { + if (event.key === "Escape") { + closeModal('delete-watchlist-modal'); + } + }); } async function deleteSelectedWatchlists() { - showSpinner(); - const currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); - const selectedCheckboxes = document.querySelectorAll('#delete-watchlist-checkboxes-container input[type="checkbox"]:checked'); - const selectedIds = Array.from(selectedCheckboxes).map(checkbox => checkbox.value); + try { + showSpinner(); + const currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); + const selectedCheckboxes = document.querySelectorAll('#delete-watchlist-checkboxes-container input[type="checkbox"]:checked'); + const selectedIds = Array.from(selectedCheckboxes).map(checkbox => checkbox.value); - if (currentUserEmail) { - for (const id of selectedIds) { - await deleteDoc(doc(db, 'watchlists', id)); + if (currentUserEmail) { + const q = query(collection(db, "watchlists"), where("userEmail", "==", currentUserEmail)); + const querySnapshot = await getDocs(q); + + for (const id of selectedIds) { + await deleteDoc(doc(db, 'watchlists', id)); + } } + else { + let watchlists = JSON.parse(localStorage.getItem('localWatchlists')) || []; + watchlists = watchlists.filter(watchlist => !selectedIds.includes(watchlist.id)); + localStorage.setItem('localWatchlists', JSON.stringify(watchlists)); + } + + closeModal('delete-watchlist-modal'); + loadWatchLists(); + hideSpinner(); + window.location.reload(); } - else { - let watchlists = JSON.parse(localStorage.getItem('localWatchlists')) || []; - watchlists = watchlists.filter(watchlist => !selectedIds.includes(watchlist.id)); - localStorage.setItem('localWatchlists', JSON.stringify(watchlists)); - } + catch (error) { + if (error.code === 'resource-exhausted') { + showSpinner(); + const selectedCheckboxes = document.querySelectorAll('#delete-watchlist-checkboxes-container input[type="checkbox"]:checked'); + const selectedIds = Array.from(selectedCheckboxes).map(checkbox => checkbox.value); - closeModal('delete-watchlist-modal'); - loadWatchLists(); - hideSpinner(); - window.location.reload(); + let watchlists = JSON.parse(localStorage.getItem('localWatchlists')) || []; + watchlists = watchlists.filter(watchlist => !selectedIds.includes(watchlist.id)); + localStorage.setItem('localWatchlists', JSON.stringify(watchlists)); + + closeModal('delete-watchlist-modal'); + loadWatchLists(); + hideSpinner(); + window.location.reload(); + } + } } document.getElementById('delete-watchlist-btn').addEventListener('click', populateDeleteModal); @@ -854,7 +1220,9 @@ async function fetchMovieDetails(movieId) { try { const response = await fetch(url); const movie = await response.json(); - return createMovieCard(movie); + const movieCard = createMovieCard(movie); + movieCard.setAttribute('data-movie-title', movie.title); + return movieCard; } catch (error) { const errorDiv = document.createElement('div'); @@ -867,21 +1235,40 @@ function createMovieCard(movie) { const movieEl = document.createElement('div'); movieEl.classList.add('movie'); movieEl.style.cursor = 'pointer'; + movieEl.style.zIndex = '1000'; + + let movieTitle = movie.title; + const words = movieTitle.split(' '); + if (words.length >= 9) { + words[8] = '...'; + movieTitle = words.slice(0, 9).join(' '); + } + + const ratingClass = movie.vote_count === 0 ? "unrated" : getClassByRate(movie.vote_average); + const voteAvg = movie.vote_count === 0 ? "Unrated" : movie.vote_average.toFixed(1); + + let overview = movie.overview; + if (overview === "") { + overview = "No overview available."; + } movieEl.innerHTML = ` ${movie.title} -
-

${movie.title}

- ${movie.vote_average.toFixed(1)} +
+

${movieTitle}

+ ${voteAvg}
-
-

Movie Overview:

- ${movie.overview} +
+

Movie Overview:

+ ${overview}
`; + movieEl.addEventListener('click', () => { localStorage.setItem('selectedMovieId', movie.id); - window.location.href = 'movie-details.html'; + updateUniqueMoviesViewed(movie.id); + updateFavoriteGenre(movie.genre_ids); updateMovieVisitCount(movie.id, movie.title); + window.location.href = 'movie-details.html'; }); return movieEl; @@ -914,267 +1301,234 @@ function handleSearch() { window.location.href = 'search.html'; } -async function getMovies(url) { - const numberOfMovies = calculateMoviesToDisplay(); - const pagesToFetch = numberOfMovies <= 20 ? 1 : 2; - let allMovies = []; +async function loadWatchLists() { + const displaySection = document.getElementById('watchlists-display-section'); - for (let page = 1; page <= pagesToFetch; page++) { - const response = await fetch(`${url}&page=${page}`); - const data = await response.json(); - allMovies = allMovies.concat(data.results); - } + try { + showSpinner(); + + const currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); - const popularityThreshold = 0.5; + if (currentUserEmail) { + const q = query(collection(db, "watchlists"), where("userEmail", "==", currentUserEmail)); + const querySnapshot = await getDocs(q); + const watchlists = querySnapshot.docs.map(doc => ({id: doc.id, ...doc.data()})); - allMovies.sort((a, b) => { - const popularityDifference = Math.abs(a.popularity - b.popularity); - if (popularityDifference < popularityThreshold) { - return b.vote_average - a.vote_average; + if (watchlists.length === 0) { + displaySection.innerHTML = '

No watch lists found. Click on "Create Watch Lists" to start adding movies.

'; + } + else { + watchlists.sort((a, b) => a.order - b.order); + watchlists.sort((a, b) => (b.pinned === a.pinned) ? 0 : b.pinned ? 1 : -1); + for (const watchlist of watchlists) { + const watchlistDiv = await createWatchListDiv(watchlist); + if (watchlist.pinned) { + watchlistDiv.classList.add('pinned'); + } + displaySection.appendChild(watchlistDiv); + } + } } - return b.popularity - a.popularity; - }); + else { + let localWatchlists = JSON.parse(localStorage.getItem('localWatchlists')) || []; - if (allMovies.length > 0) { - showMovies(allMovies.slice(0, numberOfMovies)); - } - else { - searchResultsMain.innerHTML = `

No movie with the specified search term found. Please try again.

`; - } -} + if (localWatchlists.length === 0) { + displaySection.innerHTML = '

No watch lists found. Start by adding movies to your watchlist.

'; + } + else { + localWatchlists.sort((a, b) => (b.pinned === a.pinned) ? 0 : b.pinned ? 1 : -1); + for (const watchlist of localWatchlists) { + const watchlistDiv = await createWatchListDiv(watchlist); + if (watchlist.pinned) { + watchlistDiv.classList.add('pinned'); + } + displaySection.appendChild(watchlistDiv); + } + } + } -function showMovies(movies){ - searchResultsMain.innerHTML = ''; - movies.forEach((movie) => { - const { id, poster_path, title, vote_average, overview } = movie; - const movieE1 = document.createElement('div'); - const voteAverage = vote_average.toFixed(1); - movieE1.classList.add('movie'); - - const movieImage = poster_path - ? `${title}` - : `
Image Not Available
`; - - movieE1.innerHTML = ` - ${movieImage} -
-

${title}

- ${voteAverage} -
-
-

Movie Overview:

- ${overview} -
`; - - movieE1.addEventListener('click', () => { - localStorage.setItem('selectedMovieId', id); - window.location.href = 'movie-details.html'; - updateMovieVisitCount(id, title); - }); + let favorites = []; + let favoritesTVSeries = []; - searchResultsMain.appendChild(movieE1); - }); -} + if (currentUserEmail) { + const usersRef = query(collection(db, "MovieVerseUsers"), where("email", "==", currentUserEmail)); + const userSnapshot = await getDocs(usersRef); -function calculateMoviesToDisplay() { - const screenWidth = window.innerWidth; - if (screenWidth <= 689.9) return 10; - if (screenWidth <= 1021.24) return 20; - if (screenWidth <= 1353.74) return 21; - if (screenWidth <= 1684.9) return 20; - if (screenWidth <= 2017.49) return 20; - if (screenWidth <= 2349.99) return 18; - if (screenWidth <= 2681.99) return 21; - if (screenWidth <= 3014.49) return 24; - if (screenWidth <= 3345.99) return 27; - if (screenWidth <= 3677.99) return 20; - if (screenWidth <= 4009.99) return 22; - if (screenWidth <= 4340.99) return 24; - if (screenWidth <= 4673.49) return 26; - if (screenWidth <= 5005.99) return 28; - if (screenWidth <= 5337.99) return 30; - if (screenWidth <= 5669.99) return 32; - if (screenWidth <= 6001.99) return 34; - if (screenWidth <= 6333.99) return 36; - if (screenWidth <= 6665.99) return 38; - if (screenWidth <= 6997.99) return 40; - if (screenWidth <= 7329.99) return 42; - if (screenWidth <= 7661.99) return 44; - if (screenWidth <= 7993.99) return 46; - if (screenWidth <= 8325.99) return 48; - return 20; -} + if (!userSnapshot.empty) { + const userData = userSnapshot.docs[0].data(); + favorites = userData.favoritesMovies || []; + favoritesTVSeries = userData.favoritesTVSeries || []; + } + } + else { + favorites = JSON.parse(localStorage.getItem('moviesFavorited')) || []; + favoritesTVSeries = JSON.parse(localStorage.getItem('favoritesTVSeries')) || []; + } -function handleSignInOut() { - const isSignedIn = JSON.parse(localStorage.getItem('isSignedIn')) || false; + if (favorites.length > 0) { + const favoritesDiv = document.createElement('div'); + favoritesDiv.className = 'watchlist'; + favoritesDiv.id = 'favorites-watchlist'; - if (isSignedIn) { - localStorage.setItem('isSignedIn', JSON.stringify(false)); - alert('You have been signed out.'); - } - else { - window.location.href = 'sign-in.html'; - return; - } + const title = document.createElement('h3'); + title.textContent = "Favorite Movies"; + title.className = 'watchlist-title'; - updateSignInButtonState(); -} + const description = document.createElement('p'); + description.textContent = "A collection of your favorite movies."; + description.className = 'watchlist-description'; -function updateSignInButtonState() { - const isSignedIn = JSON.parse(localStorage.getItem('isSignedIn')) || false; + favoritesDiv.appendChild(title); + favoritesDiv.appendChild(description); - const signInText = document.getElementById('signInOutText'); - const signInIcon = document.getElementById('signInIcon'); - const signOutIcon = document.getElementById('signOutIcon'); + const moviesContainer = document.createElement('div'); + moviesContainer.className = 'movies-container'; - if (isSignedIn) { - signInText.textContent = 'Sign Out'; - signInIcon.style.display = 'none'; - signOutIcon.style.display = 'inline-block'; - } - else { - signInText.textContent = 'Sign In'; - signInIcon.style.display = 'inline-block'; - signOutIcon.style.display = 'none'; - } -} + for (const movieId of favorites) { + const movieCard = await fetchMovieDetails(movieId); + moviesContainer.appendChild(movieCard); + } -document.addEventListener("DOMContentLoaded", function() { - updateSignInButtonState(); - document.getElementById('googleSignInBtn').addEventListener('click', handleSignInOut); -}); + favoritesDiv.appendChild(moviesContainer); + displaySection.appendChild(favoritesDiv); + } + else { + const favoritesDiv = document.createElement('div'); + favoritesDiv.className = 'watchlist'; + favoritesDiv.id = 'favorites-watchlist'; + favoritesDiv.innerHTML = '

Favorite Movies

No favorite movies added yet.

'; + displaySection.appendChild(favoritesDiv); + } -async function loadWatchLists() { - showSpinner(); + if (favoritesTVSeries.length > 0) { + const favoritesDiv = document.createElement('div'); + favoritesDiv.className = 'watchlist'; + favoritesDiv.id = 'favorites-tvseries-watchlist'; - const currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); - const displaySection = document.getElementById('watchlists-display-section'); + const title = document.createElement('h3'); + title.textContent = "Favorite TV Series"; + title.className = 'watchlist-title'; - if (currentUserEmail) { - const q = query(collection(db, "watchlists"), where("userEmail", "==", currentUserEmail)); - const querySnapshot = await getDocs(q); - const watchlists = querySnapshot.docs.map(doc => ({ id: doc.id, ...doc.data() })); + const description = document.createElement('p'); + description.textContent = "A collection of your favorite TV series."; + description.className = 'watchlist-description'; - if (watchlists.length === 0) { - displaySection.innerHTML = '

No watch lists found. Click on "Create a Watch List" to start adding movies.

'; + favoritesDiv.appendChild(title); + favoritesDiv.appendChild(description); + + const moviesContainer = document.createElement('div'); + moviesContainer.className = 'movies-container'; + + for (const tvSeriesId of favoritesTVSeries) { + const tvSeriesCard = await fetchTVSeriesDetails(tvSeriesId); + moviesContainer.appendChild(tvSeriesCard); + } + + favoritesDiv.appendChild(moviesContainer); + displaySection.appendChild(favoritesDiv); } else { - watchlists.sort((a, b) => a.order - b.order); - watchlists.sort((a, b) => (b.pinned === a.pinned) ? 0 : b.pinned ? 1 : -1); - for (const watchlist of watchlists) { - const watchlistDiv = await createWatchListDiv(watchlist); - if (watchlist.pinned) { - watchlistDiv.classList.add('pinned'); - } - displaySection.appendChild(watchlistDiv); - } + const favoritesDiv = document.createElement('div'); + favoritesDiv.className = 'watchlist'; + favoritesDiv.id = 'favorites-tvseries-watchlist'; + favoritesDiv.innerHTML = '

Favorite TV Series

No favorite TV series added yet.

'; + displaySection.appendChild(favoritesDiv); } + + hideSpinner(); } - else { - let localWatchlists = JSON.parse(localStorage.getItem('localWatchlists')) || []; - if (localWatchlists.length === 0) { - displaySection.innerHTML = '

No watch lists found. Start by adding movies to your watchlist.

'; - } - else { - localWatchlists.sort((a, b) => (b.pinned === a.pinned) ? 0 : b.pinned ? 1 : -1); - for (const watchlist of localWatchlists) { - const watchlistDiv = await createWatchListDiv(watchlist); - if (watchlist.pinned) { - watchlistDiv.classList.add('pinned'); + catch (error) { + if (error.code === 'resource-exhausted') { + let localWatchlists = JSON.parse(localStorage.getItem('localWatchlists')) || []; + + if (localWatchlists.length === 0) { + displaySection.innerHTML = '

No watch lists found. Start by adding movies to your watchlist.

'; + } + else { + localWatchlists.sort((a, b) => (b.pinned === a.pinned) ? 0 : b.pinned ? 1 : -1); + for (const watchlist of localWatchlists) { + const watchlistDiv = await createWatchListDiv(watchlist); + if (watchlist.pinned) { + watchlistDiv.classList.add('pinned'); + } + displaySection.appendChild(watchlistDiv); } - displaySection.appendChild(watchlistDiv); } - } - } - let favorites = []; - let favoritesTVSeries = []; - if (currentUserEmail) { - const usersRef = query(collection(db, "MovieVerseUsers"), where("email", "==", currentUserEmail)); - const userSnapshot = await getDocs(usersRef); + let favorites = []; + let favoritesTVSeries = []; - if (!userSnapshot.empty) { - const userData = userSnapshot.docs[0].data(); - favorites = userData.favoritesMovies || []; - favoritesTVSeries = userData.favoritesTVSeries || []; - } - } - else { - favorites = JSON.parse(localStorage.getItem('favoritesMovies')) || []; - favoritesTVSeries = JSON.parse(localStorage.getItem('favoritesTVSeries')) || []; - } + favorites = JSON.parse(localStorage.getItem('moviesFavorited')) || []; + favoritesTVSeries = JSON.parse(localStorage.getItem('favoritesTVSeries')) || []; - if (favorites.length > 0) { - const favoritesDiv = document.createElement('div'); - favoritesDiv.className = 'watchlist'; - favoritesDiv.id = 'favorites-watchlist'; + if (favorites.length > 0) { + const favoritesDiv = document.createElement('div'); + favoritesDiv.className = 'watchlist'; + favoritesDiv.id = 'favorites-watchlist'; - const title = document.createElement('h3'); - title.textContent = "Favorite Movies"; - title.className = 'watchlist-title'; + const title = document.createElement('h3'); + title.textContent = "Favorite Movies"; + title.className = 'watchlist-title'; - const description = document.createElement('p'); - description.textContent = "A collection of your favorite movies."; - description.className = 'watchlist-description'; + const description = document.createElement('p'); + description.textContent = "A collection of your favorite movies."; + description.className = 'watchlist-description'; - favoritesDiv.appendChild(title); - favoritesDiv.appendChild(description); + favoritesDiv.appendChild(title); + favoritesDiv.appendChild(description); - const moviesContainer = document.createElement('div'); - moviesContainer.className = 'movies-container'; + const moviesContainer = document.createElement('div'); + moviesContainer.className = 'movies-container'; - for (const movieId of favorites) { - const movieCard = await fetchMovieDetails(movieId); - moviesContainer.appendChild(movieCard); - } + for (const movieId of favorites) { + const movieCard = await fetchMovieDetails(movieId); + moviesContainer.appendChild(movieCard); + } - favoritesDiv.appendChild(moviesContainer); - displaySection.appendChild(favoritesDiv); - } - else { - const favoritesDiv = document.createElement('div'); - favoritesDiv.className = 'watchlist'; - favoritesDiv.id = 'favorites-watchlist'; - favoritesDiv.innerHTML = '

Favorite Movies

No favorite movies added yet.

'; - displaySection.appendChild(favoritesDiv); - } + favoritesDiv.appendChild(moviesContainer); + displaySection.appendChild(favoritesDiv); + } + else { + const favoritesDiv = document.createElement('div'); + favoritesDiv.className = 'watchlist'; + favoritesDiv.id = 'favorites-watchlist'; + favoritesDiv.innerHTML = '

Favorite Movies

No favorite movies added yet.

'; + displaySection.appendChild(favoritesDiv); + } - if (favoritesTVSeries.length > 0) { - const favoritesDiv = document.createElement('div'); - favoritesDiv.className = 'watchlist'; - favoritesDiv.id = 'favorites-tvseries-watchlist'; + if (favoritesTVSeries.length > 0) { + const favoritesDiv = document.createElement('div'); + favoritesDiv.className = 'watchlist'; + favoritesDiv.id = 'favorites-tvseries-watchlist'; - const title = document.createElement('h3'); - title.textContent = "Favorite TV Series"; - title.className = 'watchlist-title'; + const title = document.createElement('h3'); + title.textContent = "Favorite TV Series"; + title.className = 'watchlist-title'; - const description = document.createElement('p'); - description.textContent = "A collection of your favorite TV series."; - description.className = 'watchlist-description'; + const description = document.createElement('p'); + description.textContent = "A collection of your favorite TV series."; + description.className = 'watchlist-description'; - favoritesDiv.appendChild(title); - favoritesDiv.appendChild(description); + favoritesDiv.appendChild(title); + favoritesDiv.appendChild(description); - const moviesContainer = document.createElement('div'); - moviesContainer.className = 'movies-container'; + const moviesContainer = document.createElement('div'); + moviesContainer.className = 'movies-container'; - for (const tvSeriesId of favoritesTVSeries) { - const tvSeriesCard = await fetchTVSeriesDetails(tvSeriesId); - moviesContainer.appendChild(tvSeriesCard); - } + for (const tvSeriesId of favoritesTVSeries) { + const tvSeriesCard = await fetchTVSeriesDetails(tvSeriesId); + moviesContainer.appendChild(tvSeriesCard); + } - favoritesDiv.appendChild(moviesContainer); - displaySection.appendChild(favoritesDiv); - } - else { - const favoritesDiv = document.createElement('div'); - favoritesDiv.className = 'watchlist'; - favoritesDiv.id = 'favorites-tvseries-watchlist'; - favoritesDiv.innerHTML = '

Favorite TV Series

No favorite TV series added yet.

'; - displaySection.appendChild(favoritesDiv); + favoritesDiv.appendChild(moviesContainer); + displaySection.appendChild(favoritesDiv); + hideSpinner(); + } + } + else { + console.error('An error occurred:', error); + } } - - hideSpinner(); } async function fetchTVSeriesDetails(tvSeriesId) { @@ -1183,12 +1537,14 @@ async function fetchTVSeriesDetails(tvSeriesId) { try { const response = await fetch(url); - const movie = await response.json(); - return createTVSeriesCard(movie); + const series = await response.json(); + const seriesCard = createTVSeriesCard(series); + seriesCard.setAttribute('data-series-title', series.name); + return seriesCard; } catch (error) { const errorDiv = document.createElement('div'); - errorDiv.textContent = 'Error loading movie details. Please try refreshing the page.'; + errorDiv.textContent = 'Error loading series details. Please try refreshing the page.'; return errorDiv; } } @@ -1197,26 +1553,62 @@ function createTVSeriesCard(movie) { const movieEl = document.createElement('div'); movieEl.classList.add('movie'); movieEl.style.cursor = 'pointer'; + movieEl.style.zIndex = '1000'; + + let movieTitle = movie.name; + const words = movieTitle.split(' '); + if (words.length >= 9) { + words[8] = '...'; + movieTitle = words.slice(0, 9).join(' '); + } + + const ratingClass = movie.vote_count === 0 ? "unrated" : getClassByRate(movie.vote_average); + const voteAvg = movie.vote_count === 0 ? "Unrated" : movie.vote_average.toFixed(1); + + let overview = movie.overview; + if (overview === "") { + overview = "No overview available."; + } movieEl.innerHTML = ` - ${movie.title} -
-

${movie.name}

- ${movie.vote_average.toFixed(1)} + ${movie.name} +
+

${movieTitle}

+ ${voteAvg}
-
-

Movie Overview:

- ${movie.overview} +
+

TV Series Overview:

+ ${overview}
`; + movieEl.addEventListener('click', () => { localStorage.setItem('selectedTvSeriesId', movie.id); + updateMovieVisitCount(movie.id, movie.name); + updateUniqueMoviesViewed(movie.id); + updateFavoriteGenre(movie.genres_ids); window.location.href = 'tv-details.html'; - updateMovieVisitCount(movie.id, movie.title); }); return movieEl; } +function updateFavoriteGenre(genre_ids) { + if (genre_ids && genre_ids.length > 0) { + const favoriteGenres = JSON.parse(localStorage.getItem('favoriteGenres')) || []; + favoriteGenres.push(genre_ids[0]); + localStorage.setItem('favoriteGenres', JSON.stringify(favoriteGenres)); + } +} + +function updateUniqueMoviesViewed(movieId) { + let viewedMovies = JSON.parse(localStorage.getItem('uniqueMoviesViewed')) || []; + + if (!viewedMovies.includes(movieId)) { + viewedMovies.push(movieId); + localStorage.setItem('uniqueMoviesViewed', JSON.stringify(viewedMovies)); + } +} + function showSpinner() { document.getElementById('myModal').classList.add('modal-visible'); } @@ -1285,12 +1677,64 @@ function addWatchListControls(watchlistDiv, watchlistId) { moveDownBtn.onclick = function() { moveWatchList(watchlistDiv, false); }; moveDownBtn.title = 'Move this watch list down'; + const shareBtn = document.createElement('button'); + shareBtn.innerHTML = ''; + shareBtn.title = 'Share this watch list'; + shareBtn.onclick = function() { shareWatchList(watchlistDiv); }; + controlContainer.appendChild(pinBtn); controlContainer.appendChild(moveUpBtn); controlContainer.appendChild(moveDownBtn); + controlContainer.appendChild(shareBtn); watchlistDiv.appendChild(controlContainer); } +function shareWatchList(watchlistDiv) { + const watchlistTitle = watchlistDiv.querySelector('.watchlist-title').textContent; + let itemsToShare = `Explore my curated watchlist, "${watchlistTitle}", which contains:\n`; + let finalLine = 'Happy Watching! 🍿🎬🎥\n\n' + + const movieCards = watchlistDiv.querySelectorAll('[data-movie-title]'); + const tvSeriesCards = watchlistDiv.querySelectorAll('[data-series-title]'); + + movieCards.forEach(movieCard => { + itemsToShare += `- ${movieCard.getAttribute('data-movie-title')}\n`; + }); + + tvSeriesCards.forEach(seriesCard => { + itemsToShare += `- ${seriesCard.getAttribute('data-series-title')}\n`; + }); + + itemsToShare += finalLine; + + if (navigator.share) { + navigator.share({ + title: `Share Watchlist: ${watchlistTitle}`, + text: itemsToShare + }).catch(err => { + console.error('Error sharing the watchlist:', err); + }); + } + else { + downloadWatchlist(watchlistTitle, itemsToShare); + } +} + +function downloadWatchlist(title, content) { + const encodedContent = encodeURIComponent(content); + const dataUri = `data:text/plain;charset=utf-8,${encodedContent}`; + + const element = document.createElement('a'); + element.setAttribute('href', dataUri); + element.setAttribute('download', `${title.replace(/[\s]+/g, '_')}.txt`); + + element.style.display = 'none'; + document.body.appendChild(element); + + element.click(); + document.body.removeChild(element); +} + function createWatchListDiv(watchlist) { const watchlistDiv = document.createElement('div'); watchlistDiv.className = 'watchlist'; @@ -1405,6 +1849,7 @@ async function moveWatchList(watchlistDiv, moveUp) { async function pinWatchList(watchlistDiv, watchlistId) { showSpinner(); + const isPinned = watchlistDiv.classList.contains('pinned'); const currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); diff --git a/MovieVerse-Mobile/app/js/firebase.js b/MovieVerse-Mobile/app/js/firebase.js new file mode 100644 index 00000000..590efb98 --- /dev/null +++ b/MovieVerse-Mobile/app/js/firebase.js @@ -0,0 +1,50 @@ +import { initializeApp, getApps, getApp } from "https://www.gstatic.com/firebasejs/10.8.0/firebase-app.js"; +import { getFirestore } from "https://www.gstatic.com/firebasejs/10.8.0/firebase-firestore.js"; + +function translateFBC(value) { + return atob(value); +} + +function getFBConfig1() { + const fbConfig1 = "QUl6YVN5REw2a1FuU2ZVZDhVdDhIRnJwS3VpdnF6MXhkWG03aw=="; + return translateFBC(fbConfig1); +} + +const movieCode = { + part1: 'YzVhMjBjODY=', + part2: 'MWFjZjdiYjg=', + part3: 'ZDllOTg3ZGNjN2YxYjU1OA==' +}; + +function getFBConfig2() { + const fbConfig2 = "bW92aWV2ZXJzZS1hcHAuZmlyZWJhc2VhcHAuY29t"; + return translateFBC(fbConfig2); +} + +function getFBConfig3() { + const fbConfig3 = "bW92aWV2ZXJzZS1hcHAuYXBwc3BvdC5jb20="; + return translateFBC(fbConfig3); +} + +function getFBConfig4() { + const fbConfig4 = "ODAyOTQzNzE4ODcx"; + return translateFBC(fbConfig4); +} + +function getFBConfig5() { + const fbConfig5 = "MTo4MDI5NDM3MTg4NzE6d2ViOjQ4YmM5MTZjYzk5ZTI3MjQyMTI3OTI="; + return translateFBC(fbConfig5); +} + +const firebaseConfig = { + apiKey: getFBConfig1(), + authDomain: getFBConfig2(), + projectId: "movieverse-app", + storageBucket: getFBConfig3(), + messagingSenderId: getFBConfig4(), + appId: getFBConfig5() +}; + +export const app = !getApps().length ? initializeApp(firebaseConfig) : getApp(); + +export const db = getFirestore(app); diff --git a/MovieVerse-Mobile/app/js/inception.js b/MovieVerse-Mobile/app/js/inception.js index 4ff1337e..410660ef 100644 --- a/MovieVerse-Mobile/app/js/inception.js +++ b/MovieVerse-Mobile/app/js/inception.js @@ -24,18 +24,7 @@ const searchTitle = document.getElementById("search-title"); let trailerUrlGlobal; let initialMainContent; - -function getClassByRate(vote){ - if (vote >= 8) { - return 'green'; - } - else if (vote >= 5) { - return 'orange'; - } - else { - return 'red'; - } -} +let trailerButton; form.addEventListener('submit', (e) => { e.preventDefault(); @@ -68,7 +57,7 @@ async function fetchGenreMap() { localStorage.setItem('genreMap', JSON.stringify(genreMap)); } catch (error) { - console.error('Error fetching genre map:', error); + console.log('Error fetching genre map:', error); } } @@ -100,7 +89,7 @@ async function rotateUserStats() { { label: "Favorite Movies", getValue: () => { - const favoritedMovies = JSON.parse(localStorage.getItem('favoritesMovies')) || []; + const favoritedMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; return favoritedMovies.length; } }, @@ -108,8 +97,37 @@ async function rotateUserStats() { label: "Favorite Genre", getValue: () => { const mostCommonGenreCode = getMostCommonGenre(); - const genreMap = JSON.parse(localStorage.getItem('genreMap')) || {}; - return genreMap[mostCommonGenreCode] || 'Not Available'; + const genreMapString = localStorage.getItem('genreMap'); + if (!genreMapString) { + 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'; + } + + let genreObject; + if (Array.isArray(genreMap)) { + genreObject = genreMap.reduce((acc, genre) => { + acc[genre.id] = genre.name; + return acc; + }, {}); + } + 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'; + } + + return genreObject[mostCommonGenreCode] || 'Not Available'; } }, { label: "Watchlists Created", getValue: () => localStorage.getItem('watchlistsCreated') || 0 }, @@ -591,7 +609,7 @@ async function fetchMovieDetails(movieId) { updateBrowserURL(movie.title); } catch (error) { - console.error('Error fetching movie details:', error); + console.log('Error fetching movie details:', error); } } @@ -740,7 +758,7 @@ async function fetchMovieRatings(imdbId, tmdbMovieData) { populateMovieDetails(tmdbMovieData, imdbRating, rtRating, metascore, awards, rated); } catch (error) { - console.error('Error fetching movie ratings:', error); + console.log('Error fetching movie ratings:', error); const fallbackImdbRating = (tmdbMovieData.vote_average / 2).toFixed(1) * 2; populateMovieDetails(tmdbMovieData, fallbackImdbRating, 'N/A', 'No metascore information available', 'No awards information available'); } @@ -1189,7 +1207,7 @@ async function showMovieOfTheDay() { } } catch (error) { - console.error('Error fetching movie:', error); + console.log('Error fetching movie:', error); fallbackMovieSelection(); } } diff --git a/MovieVerse-Mobile/app/js/leonardo-dicarprio.js b/MovieVerse-Mobile/app/js/leonardo-dicarprio.js index cb6cf51c..13e252ac 100644 --- a/MovieVerse-Mobile/app/js/leonardo-dicarprio.js +++ b/MovieVerse-Mobile/app/js/leonardo-dicarprio.js @@ -116,7 +116,7 @@ async function fetchActorDetails(actorId) { } } catch (error) { - console.error('Error fetching actor details:', error); + console.log('Error fetching actor details:', error); document.getElementById('actor-details-container').innerHTML = '

Error fetching actor details

'; } } @@ -219,7 +219,7 @@ async function fetchGenreMap() { localStorage.setItem('genreMap', JSON.stringify(genreMap)); } catch (error) { - console.error('Error fetching genre map:', error); + console.log('Error fetching genre map:', error); } } @@ -251,7 +251,7 @@ async function rotateUserStats() { { label: "Favorite Movies", getValue: () => { - const favoritedMovies = JSON.parse(localStorage.getItem('favoritesMovies')) || []; + const favoritedMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; return favoritedMovies.length; } }, @@ -259,8 +259,37 @@ async function rotateUserStats() { label: "Favorite Genre", getValue: () => { const mostCommonGenreCode = getMostCommonGenre(); - const genreMap = JSON.parse(localStorage.getItem('genreMap')) || {}; - return genreMap[mostCommonGenreCode] || 'Not Available'; + const genreMapString = localStorage.getItem('genreMap'); + if (!genreMapString) { + 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'; + } + + let genreObject; + if (Array.isArray(genreMap)) { + genreObject = genreMap.reduce((acc, genre) => { + acc[genre.id] = genre.name; + return acc; + }, {}); + } + 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'; + } + + return genreObject[mostCommonGenreCode] || 'Not Available'; } }, { label: "Watchlists Created", getValue: () => localStorage.getItem('watchlistsCreated') || 0 }, @@ -452,7 +481,7 @@ async function showMovieOfTheDay() { } } catch (error) { - console.error('Error fetching movie:', error); + console.log('Error fetching movie:', error); fallbackMovieSelection(); } } diff --git a/MovieVerse-Mobile/app/js/movie-details.js b/MovieVerse-Mobile/app/js/movie-details.js index 7f181677..1769cbc2 100644 --- a/MovieVerse-Mobile/app/js/movie-details.js +++ b/MovieVerse-Mobile/app/js/movie-details.js @@ -27,12 +27,13 @@ const form = document.getElementById("form1"); const SEARCHPATH = `https://${getMovieVerseData()}/3/search/movie?&${generateMovieNames()}${getMovieCode()}&query=`; const main = document.getElementById("main"); -const IMGPATH = "https://image.tmdb.org/t/p/w1280"; +const IMGPATH = "https://image.tmdb.org/t/p/w780"; const favoriteButton = document.getElementById("favorite-btn"); const searchTitle = document.getElementById("search-title"); let trailerUrlGlobal; let initialMainContent; +let trailerButton; function getClassByRate(vote){ if (vote >= 8) { @@ -59,108 +60,6 @@ function handleSearch() { window.location.href = 'search.html'; } -function calculateMoviesToDisplay() { - const screenWidth = window.innerWidth; - if (screenWidth <= 689.9) return 10; - if (screenWidth <= 1021.24) return 20; - if (screenWidth <= 1353.74) return 21; - if (screenWidth <= 1684.9) return 20; - if (screenWidth <= 2017.49) return 20; - if (screenWidth <= 2349.99) return 18; - if (screenWidth <= 2681.99) return 21; - if (screenWidth <= 3014.49) return 24; - if (screenWidth <= 3345.99) return 27; - if (screenWidth <= 3677.99) return 20; - if (screenWidth <= 4009.99) return 22; - if (screenWidth <= 4340.99) return 24; - if (screenWidth <= 4673.49) return 26; - if (screenWidth <= 5005.99) return 28; - if (screenWidth <= 5337.99) return 30; - if (screenWidth <= 5669.99) return 32; - if (screenWidth <= 6001.99) return 34; - if (screenWidth <= 6333.99) return 36; - if (screenWidth <= 6665.99) return 38; - if (screenWidth <= 6997.99) return 40; - if (screenWidth <= 7329.99) return 42; - if (screenWidth <= 7661.99) return 44; - if (screenWidth <= 7993.99) return 46; - if (screenWidth <= 8325.99) return 48; - return 20; -} - -async function getMovies(url) { - clearMovieDetails(); - const numberOfMovies = calculateMoviesToDisplay(); - const pagesToFetch = numberOfMovies <= 20 ? 1 : 2; - let allMovies = []; - - for (let page = 1; page <= pagesToFetch; page++) { - const response = await fetch(`${url}&page=${page}`); - const data = await response.json(); - allMovies = allMovies.concat(data.results); - } - - const popularityThreshold = 0.5; - - allMovies.sort((a, b) => { - const popularityDifference = Math.abs(a.popularity - b.popularity); - if (popularityDifference < popularityThreshold) { - return b.vote_average - a.vote_average; - } - return b.popularity - a.popularity; - }); - - if (allMovies.length > 0) { - showMovies(allMovies.slice(0, numberOfMovies)); - document.getElementById('clear-search-btn').style.display = 'block'; - } - else { - main.innerHTML = `

No movie with the specified search term found. Please try again.

`; - document.getElementById('clear-search-btn').style.display = 'none'; - } -} - -function clearMovieDetails() { - const movieDetailsContainer = document.getElementById('movie-details-container'); - if (movieDetailsContainer) { - movieDetailsContainer.innerHTML = ''; - } -} - -function showMovies(movies){ - main.innerHTML = ''; - movies.forEach((movie) => { - const { id, poster_path, title, vote_average, overview } = movie; - const movieE1 = document.createElement('div'); - const voteAverage = vote_average.toFixed(1); - movieE1.classList.add('movie'); - - const movieImage = poster_path - ? `${title}` - : `
Movie Image Not Available
`; - - movieE1.innerHTML = ` - ${movieImage} -
-

${title}

- ${voteAverage} -
-
-

Movie Overview:

- ${overview} -
`; - - movieE1.addEventListener('click', () => { - localStorage.setItem('selectedMovieId', id); - window.location.href = 'movie-details.html'; - updateMovieVisitCount(id, title); - }); - - main.appendChild(movieE1); - }); - applySettings(); -} - async function ensureGenreMapIsAvailable() { if (!localStorage.getItem('genreMap')) { await fetchGenreMap(); @@ -177,9 +76,10 @@ async function fetchGenreMap() { return map; }, {}); localStorage.setItem('genreMap', JSON.stringify(genreMap)); + console.log(genreMap) } catch (error) { - console.error('Error fetching genre map:', error); + console.log('Error fetching genre map:', error); } } @@ -211,7 +111,7 @@ async function rotateUserStats() { { label: "Favorite Movies", getValue: () => { - const favoritedMovies = JSON.parse(localStorage.getItem('favoritesMovies')) || []; + const favoritedMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; return favoritedMovies.length; } }, @@ -219,8 +119,37 @@ async function rotateUserStats() { label: "Favorite Genre", getValue: () => { const mostCommonGenreCode = getMostCommonGenre(); - const genreMap = JSON.parse(localStorage.getItem('genreMap')) || {}; - return genreMap[mostCommonGenreCode] || 'Not Available'; + const genreMapString = localStorage.getItem('genreMap'); + if (!genreMapString) { + 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'; + } + + let genreObject; + if (Array.isArray(genreMap)) { + genreObject = genreMap.reduce((acc, genre) => { + acc[genre.id] = genre.name; + return acc; + }, {}); + } + 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'; + } + + return genreObject[mostCommonGenreCode] || 'Not Available'; } }, { label: "Watchlists Created", getValue: () => localStorage.getItem('watchlistsCreated') || 0 }, @@ -265,12 +194,19 @@ async function rotateUserStats() { function updateMovieVisitCount(movieId, movieTitle) { let movieVisits = JSON.parse(localStorage.getItem('movieVisits')) || {}; + let uniqueMoviesViewed = JSON.parse(localStorage.getItem('uniqueMoviesViewed')) || []; if (!movieVisits[movieId]) { movieVisits[movieId] = { count: 0, title: movieTitle }; } movieVisits[movieId].count += 1; + + if (!uniqueMoviesViewed.includes(movieId)) { + uniqueMoviesViewed.push(movieId); + } + localStorage.setItem('movieVisits', JSON.stringify(movieVisits)); + localStorage.setItem('uniqueMoviesViewed', JSON.stringify(uniqueMoviesViewed)); } function getMostVisitedDirector() { @@ -350,39 +286,6 @@ function getMostCommonGenre() { document.addEventListener('DOMContentLoaded', rotateUserStats); -function setStarRating(rating) { - const stars = document.querySelectorAll('.rating .star'); - stars.forEach(star => { - star.style.color = star.dataset.value > rating ? 'white' : 'gold'; - }); - - document.getElementById('rating-value').textContent = `${rating}.0/5.0`; -} - -document.querySelectorAll('.rating .star').forEach(star => { - star.addEventListener('mouseover', (e) => { - setStarRating(e.target.dataset.value); - }); - - star.addEventListener('mouseout', () => { - const movieId = localStorage.getItem('selectedMovieId'); - const savedRatings = JSON.parse(localStorage.getItem('movieRatings')) || {}; - const movieRating = savedRatings[movieId] || 0; - setStarRating(movieRating); - }); - - star.addEventListener('click', (e) => { - const movieId = localStorage.getItem('selectedMovieId'); - const rating = e.target.dataset.value; - const savedRatings = JSON.parse(localStorage.getItem('movieRatings')) || {}; - savedRatings[movieId] = rating; - localStorage.setItem('movieRatings', JSON.stringify(savedRatings)); - setStarRating(rating); - updateAverageMovieRating(movieId, rating); - window.location.reload(); - }); -}); - function updateUniqueDirectorsViewed(directorId) { let viewedDirectors = JSON.parse(localStorage.getItem('uniqueDirectorsViewed')) || []; if (!viewedDirectors.includes(directorId)) { @@ -412,6 +315,7 @@ function updateDirectorVisitCount(directorId, directorName) { } document.addEventListener('DOMContentLoaded', () => { + showSpinner(); initialMainContent = document.getElementById('main').innerHTML; const movieId = localStorage.getItem('selectedMovieId'); @@ -422,15 +326,7 @@ document.addEventListener('DOMContentLoaded', () => { fetchMovieDetails(1011985) } - document.getElementById('clear-search-btn').style.display = 'none'; - - const savedRatings = JSON.parse(localStorage.getItem('movieRatings')) || {}; - const movieRating = savedRatings[movieId] || 0; - setStarRating(movieRating); -}); - -document.getElementById('clear-search-btn').addEventListener('click', () => { - location.reload(); + hideSpinner(); }); function handleSignInOut() { @@ -468,8 +364,10 @@ function updateSignInButtonState() { } document.addEventListener("DOMContentLoaded", function() { + showSpinner(); updateSignInButtonState(); document.getElementById('googleSignInBtn').addEventListener('click', handleSignInOut); + hideSpinner(); }); const twoLetterLangCodes = [ @@ -677,7 +575,6 @@ async function fetchMovieDetails(movieId) { const code = `${getMovieCode()}`; const url = `https://${getMovieVerseData()}/3/movie/${movieId}?${generateMovieNames()}${code}&append_to_response=credits,keywords,similar`; const url2 = `https://${getMovieVerseData()}/3/movie/${movieId}?${generateMovieNames()}${code}&append_to_response=videos`; - const imdbUrl = `https://${getMovieVerseData()}/3/movie/${movieId}?${generateMovieNames()}${code}&append_to_response=external_ids`; try { const response = await fetch(url); @@ -703,7 +600,7 @@ async function fetchMovieDetails(movieId) {

Movie details not found - Try again with a different movie

`; - console.error('Error fetching movie details:', error); + console.log('Error fetching movie details:', error); hideSpinner(); } } @@ -791,7 +688,7 @@ function getRatingDetails(rating) { break; case 'NR': details = { - color: 'grey', + color: 'white', text: 'NR (Not Rated)', description: ' - Movie has not been officially rated' }; @@ -799,7 +696,7 @@ function getRatingDetails(rating) { case 'UR': case 'Unrated': details = { - color: 'grey', + color: 'white', text: 'UR (Unrated)', description: ' - Contains content not used in the rated version' }; @@ -817,45 +714,67 @@ function getRatingDetails(rating) { } async function fetchMovieRatings(imdbId, tmdbMovieData) { - const omdbCode = `${getMovieCode2()}`; - const omdb = `https://${getMovieActor()}/?i=${imdbId}&${getMovieName()}${omdbCode}`; + showSpinner(); - try { - const response = await fetch(omdb); - const data = await response.json(); + const apiKeys = [ + await getMovieCode2(), + '58efe859', + '60a09d79', + '956e468a' + ]; - let imdbRating = data.imdbRating ? data.imdbRating : 'N/A'; + const baseURL = `https://${getMovieActor()}/?i=${imdbId}&${getMovieName()}`; - if (imdbRating === 'N/A' && tmdbMovieData.vote_average) { - imdbRating = (tmdbMovieData.vote_average / 2).toFixed(1) * 2; + async function tryFetch(apiKey) { + const url = `${baseURL}${apiKey}`; + try { + const response = await fetch(url); + if (!response.ok) throw new Error('API limit reached or other error'); + return await response.json(); } + catch (error) { + return null; + } + } - const rtRatingObj = data.Ratings.find(rating => rating.Source === "Rotten Tomatoes"); - let rtRating = rtRatingObj ? rtRatingObj.Value : 'N/A'; + let data; + for (const key of apiKeys) { + data = await tryFetch(key); + if (data) break; + } - let metascore = data.Metascore ? `${data.Metascore}/100` : 'N/A'; - let awards = data.Awards; - let rated = data.Rated ? data.Rated : 'Rating information unavailable'; + if (!data) { + populateMovieDetails(tmdbMovieData, tmdbMovieData.vote_average, 'N/A', 'Metascore information unavailable, click to search on Metacritics', 'Awards information unavailable'); + return; + } - if (awards === 'N/A') { - awards = 'No awards information available'; - } + let imdbRating = data.imdbRating ? data.imdbRating : 'N/A'; + if (imdbRating === 'N/A' || imdbRating === '0.0') { + imdbRating = 'N/A'; + } - if (metascore === 'N/A/100') { - const metacriticsRatingValue = imdbRating !== 'N/A' ? parseFloat(imdbRating) : (tmdbMovieData.vote_average / 2); - metascore = calculateFallbackMetacriticsRating(metacriticsRatingValue, tmdbMovieData.vote_average) + '/100'; - } + let rtRating = 'N/A'; - if (rtRating === 'N/A') { - const imdbRatingValue = imdbRating !== 'N/A' ? parseFloat(imdbRating) : (tmdbMovieData.vote_average / 2); - rtRating = calculateFallbackRTRating(imdbRatingValue, tmdbMovieData.vote_average) - } - populateMovieDetails(tmdbMovieData, imdbRating, rtRating, metascore, awards, rated); + let metascore = data.Metascore ? `${data.Metascore}/100` : 'N/A'; + let awards = data.Awards; + let rated = data.Rated ? data.Rated : 'Rating information unavailable'; + + if (awards === 'N/A') { + awards = 'Awards information unavailable'; } - catch (error) { - const fallbackImdbRating = (tmdbMovieData.vote_average / 2).toFixed(1) * 2; - populateMovieDetails(tmdbMovieData, fallbackImdbRating, 'N/A', 'No metascore information available', 'No awards information available'); + + if (metascore === 'N/A/100') { + const metacriticsRatingValue = imdbRating !== 'N/A' ? parseFloat(imdbRating) : (tmdbMovieData.vote_average / 2); + metascore = calculateFallbackMetacriticsRating(metacriticsRatingValue, tmdbMovieData.vote_average) + '/100'; } + + if (rtRating === 'N/A') { + const imdbRatingValue = imdbRating !== 'N/A' ? parseFloat(imdbRating) : (tmdbMovieData.vote_average / 2); + rtRating = calculateFallbackRTRating(imdbRatingValue, tmdbMovieData.vote_average) + } + + populateMovieDetails(tmdbMovieData, imdbRating, rtRating, metascore, awards, rated); + hideSpinner(); } function updateBrowserURL(title) { @@ -931,8 +850,12 @@ function getYouTubeVideoId(url) { } function positionTrailerButton() { - if (!trailerButton) + showSpinner(); + if (!trailerButton) { + document.getElementById('movie-description').style.marginTop = '-20px'; return; + } + document.getElementById('movie-description').style.marginTop = '-60px'; if (window.innerWidth <= 900) { const movieDescription = document.getElementById('movie-description'); @@ -942,6 +865,7 @@ function positionTrailerButton() { const movieRating = document.getElementById('movie-rating'); movieRating.parentNode.insertBefore(trailerButton, movieRating.nextSibling); } + hideSpinner(); } document.addEventListener('DOMContentLoaded', positionTrailerButton); @@ -996,48 +920,92 @@ function createMetacriticSlug(title) { .replace(/[^\w-]/g, ''); } -function populateMovieDetails(movie, imdbRating, rtRating, metascore, awards, rated) { - document.getElementById('movie-image').src = `https://image.tmdb.org/t/p/w1280${movie.poster_path}`; +async function fetchStreamingLinks(movieId) { + const url = `https://${getMovieVerseData()}/3/movie/${movieId}/watch/providers?${generateMovieNames()}${getMovieCode()}`; + + try { + const response = await fetch(url); + const data = await response.json(); + const results = data.results || {}; + let providersMap = {}; + + Object.values(results).forEach(region => { + if (region.flatrate) { + region.flatrate.forEach(provider => { + providersMap[provider.provider_id] = provider; + }); + } + }); + + return Object.values(providersMap).slice(0, 7); + } + catch (error) { + console.error('Error fetching streaming links:', error); + return []; + } +} + +async function populateMovieDetails(movie, imdbRating, rtRating, metascore, awards, rated) { + showSpinner(); document.getElementById('movie-title').textContent = movie.title; - const movieRating = movie.vote_average.toFixed(1); const imdbLink = `https://www.imdb.com/title/${movie.imdb_id}`; + const streamingProviders = await fetchStreamingLinks(movie.id); + const movieTitleEncoded = encodeURIComponent(movie.title); + + const streamingHTML = streamingProviders.length > 0 ? streamingProviders.map(provider => { + let providerLink; + switch(provider.provider_name.toLowerCase()) { + case 'netflix': + providerLink = `https://www.netflix.com/search?q=${movieTitleEncoded}`; + break; + case 'disney plus': + providerLink = `https://www.disneyplus.com/search?q=${movieTitleEncoded}`; + break; + case 'hbo max': + providerLink = `https://www.hbomax.com/search?q=${movieTitleEncoded}`; + break; + case 'hulu': + providerLink = `https://www.hulu.com/search?q=${movieTitleEncoded}`; + break; + case 'amazon prime video': + providerLink = `https://www.amazon.com/s?k=${movieTitleEncoded}`; + break; + case 'apple tv plus': + providerLink = `https://tv.apple.com/search?term=${movieTitleEncoded}`; + break; + case 'stan': + providerLink = `https://www.stan.com.au/search?q=${movieTitleEncoded}`; + break; + case 'player': + providerLink = `https://player.pl/szukaj?search=${movieTitleEncoded}`; + break; + default: + providerLink = `https://www.google.com/search?q=watch+${movieTitleEncoded}+on+${encodeURIComponent(provider.provider_name)}`; + break; + } - const rtLink = rtRating !== 'N/A' ? `https://www.rottentomatoes.com/m/${getRtSlug(movie.title)}` : '#'; - const metaCriticsLink = metascore !== 'N/A' ? `https://www.metacritic.com/movie/${createMetacriticSlug(movie.title)}` : '#'; + return ` + ${provider.provider_name} + `; + }).join('') + ` + JustWatch + ` : 'No streaming options available.'; + const metaCriticsLink = metascore !== 'N/A' ? `https://www.metacritic.com/search/${createMetacriticSlug(movie.title)}` : '#'; const ratingDetails = getRatingDetails(rated); const ratedElement = rated ? `

Rated: ${ratingDetails.text}${ratingDetails.description}

` : ''; - document.getElementById('movie-rating').innerHTML = ` - IMDB Rating: ${imdbRating} - `; - document.getElementById('movie-rating').style.marginTop = '120px'; + document.getElementById('movie-rating').innerHTML = ``; document.title = movie.title + " - Movie Details"; - const movieImage = document.getElementById('movie-image'); const movieDescription = document.getElementById('movie-description'); - - const metascoreElement = metascore ? `

Metascore: ${metascore}

` : ''; + const metascoreElement = metascore ? `

Metascore: ${metascore}

` : ''; const awardsElement = awards ? `

Awards: ${awards}

` : ''; - if (movie.poster_path) { - movieImage.src = IMGPATH + movie.poster_path; - movieImage.alt = movie.title; - } - else { - movieImage.style.display = 'none'; - const noImageText = document.createElement('h2'); - noImageText.textContent = 'Movie Image Not Available'; - noImageText.style.textAlign = 'center'; - noImageText.style.height = '800px'; - document.querySelector('.movie-left').appendChild(noImageText); - } - - const fullLanguage = twoLetterLangCodes.find(lang => lang.code === movie.original_language).name; - const overview = movie.overview; + const overview = movie.overview ? movie.overview : 'No overview available'; const genres = movie.genres.map(genre => genre.name).join(', '); - const releaseDate = movie.release_date; + const releaseDate = movie.release_date ? movie.release_date : 'Release date not available'; const budget = movie.budget === 0 ? 'Information Not Available' : `$${movie.budget.toLocaleString()}`; const revenue = movie.revenue <= 1000 ? 'Information Not Available' : `$${movie.revenue.toLocaleString()}`; @@ -1045,35 +1013,31 @@ function populateMovieDetails(movie, imdbRating, rtRating, metascore, awards, ra const languages = movie.spoken_languages.map(lang => lang.name).join(', '); const countries = movie.production_countries.map(country => country.name).join(', '); - const originalLanguage = fullLanguage; const popularityScore = movie.popularity.toFixed(0); - const status = movie.status; - const voteCount = movie.vote_count.toLocaleString(); let keywords = movie.keywords ? movie.keywords.keywords.map(kw => kw.name).join(', ') : 'None Available'; - const similarTitles = movie.similar ? movie.similar.results.map(m => m.title).join(', ') : 'None Available'; const scaledRating = (movie.vote_average / 2).toFixed(1); if (keywords.length === 0) { - keywords = 'None Available'; + keywords = 'No keywords have been added'; } const popularityThreshold = 80; const isPopular = movie.popularity >= popularityThreshold; const popularityText = isPopular ? `${popularityScore} (This movie is popular)` : `${popularityScore} (This movie is unpopular)`; - const adultContentIndicator = movie.adult - ? `Adult Content` - : `General Audience`; - const movieStatus = `

Status: ${movie.status}

`; - const runtime = movie.runtime > 0 ? movie.runtime + ' minutes' : 'Runtime Info Not Available'; + const originalTitle = movie.original_title !== movie.title ? `

Original Title: ${movie.original_title}

` : `

Original Title: ${movie.title}

`; + const tmdbRating = movie.vote_average.toFixed(1); + document.getElementById('movie-description').innerHTML += `

Description: ${overview}

+ ${originalTitle} +

Tagline: ${tagline}

Genres: ${genres}

${ratedElement} ${movieStatus} @@ -1083,30 +1047,29 @@ function populateMovieDetails(movie, imdbRating, rtRating, metascore, awards, ra

Revenue: ${revenue}

Languages: ${languages}

Countries of Production: ${countries}

-

Original Language: ${originalLanguage}

Popularity Score: ${popularityText}

-

MovieVerse User Ratings: ${scaledRating}/5.0 (based on ${movie.vote_count} votes)

+

MovieVerse User Rating: ${scaledRating}/5.0 (based on ${movie.vote_count} votes)

${awardsElement} +

TMDb Rating: ${tmdbRating}/10.0

${metascoreElement} -

Rotten Tomatoes: ${rtRating}

-

Tagline: ${tagline}

`; if (movie.credits && movie.credits.crew) { - const director = movie.credits.crew.find(member => member.job === 'Director'); - if (director) { - const directorAge = director.birthday ? calculateAge(director.birthday) : 'N/A'; - const directorElement = document.createElement('p'); - directorElement.innerHTML = `Director: ${director.name}`; - directorElement.querySelector('.director-link').addEventListener('click', (e) => { - e.preventDefault(); - localStorage.setItem('selectedDirectorId', director.id); - document.title = `${director.name} - Director's Details`; - window.location.href = 'director-details.html'; - updateUniqueDirectorsViewed(director.id); - updateDirectorVisitCount(director.id, director.name); - }); - document.getElementById('movie-description').appendChild(directorElement); + const directors = movie.credits.crew.filter(member => member.job === 'Director'); + + if (directors.length > 0) { + const directorsLinks = directors.map(director => + `${director.name}` + ).join(', '); + + const directorsElement = document.createElement('p'); + directorsElement.innerHTML = `Director: ${directorsLinks}`; + document.getElementById('movie-description').appendChild(directorsElement); + } + else { + const noDirectorsElement = document.createElement('p'); + noDirectorsElement.innerHTML = `Director: Information not available`; + document.getElementById('movie-description').appendChild(noDirectorsElement); } } @@ -1117,18 +1080,9 @@ function populateMovieDetails(movie, imdbRating, rtRating, metascore, awards, ra if (movie.credits && movie.credits.cast.length > 0) { const topTenCast = movie.credits.cast.slice(0, 10); topTenCast.forEach((actor, index) => { - const actorLink = document.createElement('span'); - actorLink.textContent = actor.name; - actorLink.classList.add('actor-link'); - actorLink.addEventListener('click', () => { - localStorage.setItem('selectedActorId', actor.id); - window.location.href = 'actor-details.html'; - updateUniqueActorsViewed(actor.id); - updateActorVisitCount(actor.id, actor.name); - }); - + const actorLink = document.createElement('a'); + actorLink.innerHTML = `${actor.name}`; castHeading.appendChild(actorLink); - if (index < topTenCast.length - 1) { castHeading.appendChild(document.createTextNode(', ')); } @@ -1138,83 +1092,322 @@ function populateMovieDetails(movie, imdbRating, rtRating, metascore, awards, ra castHeading.appendChild(document.createTextNode('None available.')); } - const productionCompanies = movie.production_companies; - const productionCompaniesElement = document.createElement('p'); - productionCompaniesElement.innerHTML = 'Production Companies: '; - - if (productionCompanies.length === 0) { - productionCompaniesElement.innerHTML += 'None available.'; - } - productionCompanies.forEach((company, index) => { - const companyLink = document.createElement('a'); - companyLink.textContent = company.name; - companyLink.style.cursor = 'pointer'; - companyLink.style.textDecoration = 'underline'; - companyLink.href = '#'; - companyLink.classList.add('company-link'); - companyLink.addEventListener('click', (e) => { - e.preventDefault(); - localStorage.setItem('selectedCompanyId', company.id); - window.location.href = 'company-details.html'; - updateUniqueCompaniesViewed(company.id); - }); + if (movie.production_companies && movie.production_companies.length > 0) { + let companiesHTML = movie.production_companies.map(company => { + return `${company.name}`; + }).join(', '); + + const productionCompaniesElement = document.createElement('p'); + productionCompaniesElement.innerHTML = `Production Companies: ${companiesHTML}`; + document.getElementById('movie-description').appendChild(productionCompaniesElement); + } + else { + const noCompaniesElement = document.createElement('p'); + noCompaniesElement.innerHTML = `Production Companies: Information not available`; + document.getElementById('movie-description').appendChild(noCompaniesElement); + } - productionCompaniesElement.appendChild(companyLink); + if (movie.similar && movie.similar.results && movie.similar.results.length > 0) { + let similarMoviesHTML = movie.similar.results.map(similarMovie => { + return `${similarMovie.title}`; + }).join(', '); - if (index < productionCompanies.length - 1) { - productionCompaniesElement.appendChild(document.createTextNode(', ')); - } - }); + const similarMoviesElement = document.createElement('p'); + similarMoviesElement.innerHTML = `Similar Movies: ${similarMoviesHTML}`; + document.getElementById('movie-description').appendChild(similarMoviesElement); + } + else { + const noSimilarMoviesElement = document.createElement('p'); + noSimilarMoviesElement.innerHTML = `Similar Movies: None available`; + document.getElementById('movie-description').appendChild(noSimilarMoviesElement); + } - document.getElementById('movie-description').appendChild(productionCompaniesElement); - const similarMoviesHeading = document.createElement('p'); + document.getElementById('movie-description').innerHTML += ` +

Streaming Options: ${streamingHTML}

`; - similarMoviesHeading.innerHTML = 'Similar Movies: '; - document.getElementById('movie-description').appendChild(similarMoviesHeading); + const homepage = document.createElement('p'); + homepage.innerHTML = movie.homepage ? `Homepage: Visit homepage` : `Homepage: Information unavailable`; + movieDescription.appendChild(homepage); - if (movie.similar && movie.similar.results.length > 0) { - movie.similar.results.forEach((similarMovie, index) => { - const movieLink = document.createElement('span'); - movieLink.textContent = similarMovie.title; - movieLink.style.cursor = 'pointer'; - movieLink.style.textDecoration = 'underline'; - movieLink.addEventListener('mouseenter', () => { - movieLink.style.color = '#f509d9'; - }); + const keywordsElement = document.createElement('p'); + keywordsElement.innerHTML = `Keywords: ${keywords}`; + movieDescription.appendChild(keywordsElement); - movieLink.addEventListener('mouseleave', () => { - movieLink.style.color = getSavedTextColor(); - }); + createImdbRatingCircle(imdbRating, imdbLink); + + const mediaUrl = `https://${getMovieVerseData()}/3/movie/${movie.id}/images?${generateMovieNames()}${getMovieCode()}`; + const mediaResponse = await fetch(mediaUrl); + const mediaData = await mediaResponse.json(); + const images = mediaData.backdrops; + + const detailsContainer = document.getElementById('movie-description'); + + const mediaContainer = document.createElement('div'); + mediaContainer.id = 'media-container'; + mediaContainer.style = ` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + width: 90vw; + max-width: 450px; + margin: 20px auto; + overflow: hidden; + box-sizing: border-box; + `; - movieLink.addEventListener('click', () => { - localStorage.setItem('selectedMovieId', similarMovie.id); - window.location.href = 'movie-details.html'; - }); + const mediaTitle = document.createElement('p'); + mediaTitle.textContent = 'Media:'; + mediaTitle.style = ` + font-weight: bold; + align-self: start; + margin-bottom: 5px; + `; - similarMoviesHeading.appendChild(movieLink); + detailsContainer.appendChild(mediaTitle); + detailsContainer.appendChild(mediaContainer); + + const imageWrapper = document.createElement('div'); + imageWrapper.style = ` + width: 100%; + max-height: 210px; + border-radius: 16px; + overflow: hidden; + display: flex; + justify-content: center; + align-items: center; + `; - if (index < movie.similar.results.length - 1) { - similarMoviesHeading.appendChild(document.createTextNode(', ')); + const imageElement = document.createElement('img'); + imageElement.style = ` + width: 100%; + height: auto; + transition: opacity 0.5s ease-in-out; + opacity: 1; + cursor: pointer; + object-fit: contain; + `; + if (images.length > 0) { + imageElement.src = `https://image.tmdb.org/t/p/w780${images[0].file_path}`; + } + imageWrapper.appendChild(imageElement); + mediaContainer.appendChild(imageWrapper); + + imageElement.addEventListener('click', function() { + let imageUrl = this.src.replace('w780', 'w1280'); + const modalHtml = ` +
+ Movie Image + × +
+ `; + document.body.insertAdjacentHTML('beforeend', modalHtml); + const modal = document.getElementById('image-modal'); + const closeModalBtn = document.getElementById('removeBtn'); + + closeModalBtn.onclick = function() { + modal.remove(); + }; + + modal.addEventListener('click', function(event) { + if (event.target === this) { + this.remove(); } }); + }); + + const prevButton = document.createElement('button'); + prevButton.innerHTML = ''; + prevButton.style = ` + position: absolute; + left: 5px; + top: 50%; + transform: translateY(-50%); + background-color: #7378c5; + color: white; + border-radius: 8px; + height: 30px; + width: 30px; + border: none; + cursor: pointer; + z-index: 10; + `; + prevButton.onmouseover = () => prevButton.style.backgroundColor = '#ff8623'; + prevButton.onmouseout = () => prevButton.style.backgroundColor = '#7378c5'; + prevButton.onclick = () => navigateMedia(images, imageElement, -1); + mediaContainer.appendChild(prevButton); + + const nextButton = document.createElement('button'); + nextButton.innerHTML = ''; + nextButton.style = ` + position: absolute; + right: 5px; + top: 50%; + transform: translateY(-50%); + background-color: #7378c5; + color: white; + border-radius: 8px; + height: 30px; + width: 30px; + border: none; + cursor: pointer; + z-index: 10; + `; + nextButton.onmouseover = () => nextButton.style.backgroundColor = '#ff8623'; + nextButton.onmouseout = () => nextButton.style.backgroundColor = '#7378c5'; + nextButton.onclick = () => navigateMedia(images, imageElement, 1); + mediaContainer.appendChild(nextButton); + + let currentIndex = 0; + function navigateMedia(images, imgElement, direction) { + currentIndex += direction; + if (currentIndex < 0) { + currentIndex = images.length - 1; + } + else if (currentIndex >= images.length) { + currentIndex = 0; + } + imgElement.style.opacity = '0'; + setTimeout(() => { + imgElement.src = `https://image.tmdb.org/t/p/w780${images[currentIndex].file_path}`; + imgElement.style.opacity = '1'; + }, 420); + } + + if (images.length === 0) { + mediaContainer.innerHTML = '

No media available

'; + } + + const movieImage = document.getElementById('movie-image'); + if (movie.poster_path) { + movieImage.src = IMGPATH + movie.poster_path; + movieImage.alt = movie.title; + movieImage.loading = 'lazy'; } else { - similarMoviesHeading.appendChild(document.createTextNode('None available.')); + movieImage.style.display = 'none'; + const noImageText = document.createElement('h2'); + noImageText.textContent = 'Movie Image Not Available'; + noImageText.style.textAlign = 'center'; + noImageText.style.height = '800px'; + document.querySelector('.movie-left').appendChild(noImageText); } - const keywordsElement = document.createElement('p'); - keywordsElement.innerHTML = `Keywords: ${keywords}`; + hideSpinner(); +} - movieDescription.appendChild(keywordsElement); +function createImdbRatingCircle(imdbRating, imdbId) { + if (imdbRating === 'N/A' || imdbRating === null || imdbRating === undefined) { + imdbRating = 'N/A'; + } - updateMoviesFavorited(movie.id); - applySettings(); + let circleContainer = document.getElementById('imdbRatingCircleContainer'); + if (!circleContainer) { + circleContainer = document.createElement('div'); + circleContainer.id = 'imdbRatingCircleContainer'; + circleContainer.className = 'progress-container'; + const imdbLink = `${imdbId}`; + circleContainer.innerHTML = ` + +
IMDb Rating
+
+ + + + ${imdbRating} + + `; + + if (imdbRating === 'N/A') { + circleContainer.innerHTML += `

Rating information currently unavailable

`; + } + + document.getElementById('movie-description').appendChild(circleContainer); + } + else { + const text = document.getElementById('imdbRatingText'); + text.textContent = `${imdbRating}`; + } + + const circle = circleContainer.querySelector('.progress-ring__progress'); + const text = document.getElementById('imdbRatingText'); + setProgress(circle, text, imdbRating); +} + +function setProgress(circle, text, rating) { + const radius = circle.r.baseVal.value; + const circumference = radius * 2 * Math.PI; + + circle.style.transition = 'none'; + circle.style.strokeDasharray = `${circumference} ${circumference}`; + circle.style.strokeDashoffset = circumference; + + circle.getBoundingClientRect(); + + setTimeout(() => { + const offset = circumference - (rating / 10) * circumference; + circle.style.transition = 'stroke-dashoffset 0.6s ease-out, stroke 0.6s ease'; + circle.style.strokeDashoffset = offset; + circle.style.setProperty('--progress-color', rating <= 5 ? '#FF0000' : (rating >= 7.5 ? '#4CAF50' : '#2196F3')); + text.textContent = `${rating}`; + }, 10); +} + +function retriggerAnimation(imdbRating) { + const circle = document.querySelector('.progress-ring__progress'); + const text = document.getElementById('imdbRatingText'); + setProgress(circle, text, imdbRating); } function getSavedTextColor() { return localStorage.getItem('textColor') || 'white'; } +function handleDirectorClick(directorId, directorName) { + localStorage.setItem('selectedDirectorId', directorId); + document.title = `${directorName} - Director's Details`; + updateUniqueDirectorsViewed(directorId); + updateDirectorVisitCount(directorId, directorName); + window.location.href = 'director-details.html'; +} + +function selectActorId(actorId, actorName) { + const actorVisits = JSON.parse(localStorage.getItem('actorVisits')) || {}; + const uniqueActorsViewed = JSON.parse(localStorage.getItem('uniqueActorsViewed')) || []; + + if (!uniqueActorsViewed.includes(actorId)) { + uniqueActorsViewed.push(actorId); + localStorage.setItem('uniqueActorsViewed', JSON.stringify(uniqueActorsViewed)); + } + + if (actorVisits[actorId]) { + actorVisits[actorId].count++; + } + else { + actorVisits[actorId] = { count: 1, name: actorName }; + } + + localStorage.setItem('actorVisits', JSON.stringify(actorVisits)); + + localStorage.setItem('selectedActorId', actorId); + window.location.href = 'actor-details.html'; +} + +function handleCompanyClick(companyId, companyName) { + localStorage.setItem('selectedCompanyId', companyId); + document.title = `${companyName} - Company Details`; + window.location.href = 'company-details.html'; + updateUniqueCompaniesViewed(companyId); +} + +function handleSimilarMovieClick(movieId, movieTitle) { + localStorage.setItem('selectedMovieId', movieId); + document.title = `${movieTitle} - Movie Details`; + window.location.href = 'movie-details.html'; + updateMovieVisitCount(movieId, movieTitle); +} + function updateMoviesFavorited(movieId) { let favoritedMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; if (!favoritedMovies.includes(movieId)) { @@ -1327,4 +1520,4 @@ function applyTextColor(color) { .forEach(element => { element.style.color = color; }); -} \ No newline at end of file +} diff --git a/MovieVerse-Mobile/app/js/movie-match.js b/MovieVerse-Mobile/app/js/movie-match.js index f33a432c..db6cf0e0 100644 --- a/MovieVerse-Mobile/app/js/movie-match.js +++ b/MovieVerse-Mobile/app/js/movie-match.js @@ -38,7 +38,7 @@ async function showMovieOfTheDay() { } } catch (error) { - console.error('Error fetching movie:', error); + console.log('Error fetching movie:', error); fallbackMovieSelection(); } } @@ -55,7 +55,7 @@ async function fetchGenreMap() { localStorage.setItem('genreMap', JSON.stringify(genreMap)); } catch (error) { - console.error('Error fetching genre map:', error); + console.log('Error fetching genre map:', error); } } @@ -87,7 +87,7 @@ async function rotateUserStats() { { label: "Favorite Movies", getValue: () => { - const favoritedMovies = JSON.parse(localStorage.getItem('favoritesMovies')) || []; + const favoritedMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; return favoritedMovies.length; } }, @@ -95,8 +95,37 @@ async function rotateUserStats() { label: "Favorite Genre", getValue: () => { const mostCommonGenreCode = getMostCommonGenre(); - const genreMap = JSON.parse(localStorage.getItem('genreMap')) || {}; - return genreMap[mostCommonGenreCode] || 'Not Available'; + const genreMapString = localStorage.getItem('genreMap'); + if (!genreMapString) { + 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'; + } + + let genreObject; + if (Array.isArray(genreMap)) { + genreObject = genreMap.reduce((acc, genre) => { + acc[genre.id] = genre.name; + return acc; + }, {}); + } + 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'; + } + + return genreObject[mostCommonGenreCode] || 'Not Available'; } }, { label: "Watchlists Created", getValue: () => localStorage.getItem('watchlistsCreated') || 0 }, @@ -429,7 +458,6 @@ function findMovieMatch(mood, genre, period) { } const form = document.getElementById('form1'); - const IMGPATH = "https://image.tmdb.org/t/p/w1280"; const SEARCHPATH = `https://${getMovieVerseData()}/3/search/movie?&${generateMovieNames()}${getMovieCode()}&query=`; @@ -445,42 +473,3 @@ function handleSearch() { localStorage.setItem('searchQuery', searchQuery); window.location.href = 'search.html'; } - -function handleSignInOut() { - const isSignedIn = JSON.parse(localStorage.getItem('isSignedIn')) || false; - - if (isSignedIn) { - localStorage.setItem('isSignedIn', JSON.stringify(false)); - alert('You have been signed out.'); - } - else { - window.location.href = 'sign-in.html'; - return; - } - - updateSignInButtonState(); -} - -function updateSignInButtonState() { - const isSignedIn = JSON.parse(localStorage.getItem('isSignedIn')) || false; - - const signInText = document.getElementById('signInOutText'); - const signInIcon = document.getElementById('signInIcon'); - const signOutIcon = document.getElementById('signOutIcon'); - - if (isSignedIn) { - signInText.textContent = 'Sign Out'; - signInIcon.style.display = 'none'; - signOutIcon.style.display = 'inline-block'; - } - else { - signInText.textContent = 'Sign In'; - signInIcon.style.display = 'inline-block'; - signOutIcon.style.display = 'none'; - } -} - -document.addEventListener("DOMContentLoaded", function() { - updateSignInButtonState(); - document.getElementById('googleSignInBtn').addEventListener('click', handleSignInOut); -}); diff --git a/MovieVerse-Mobile/app/js/movie-timeline.js b/MovieVerse-Mobile/app/js/movie-timeline.js index 2013dabe..eabe2e25 100644 --- a/MovieVerse-Mobile/app/js/movie-timeline.js +++ b/MovieVerse-Mobile/app/js/movie-timeline.js @@ -22,7 +22,6 @@ document.getElementById('end-year').addEventListener('keydown', function(event) } }); - const movieCode = { part1: 'YzVhMjBjODY=', part2: 'MWFjZjdiYjg=', @@ -37,7 +36,7 @@ function generateMovieNames(input) { return String.fromCharCode(97, 112, 105, 95, 107, 101, 121, 61); } -const IMGPATH = "https://image.tmdb.org/t/p/w1280"; +const IMGPATH = "https://image.tmdb.org/t/p/w500"; const SEARCHPATH = `https://${getMovieVerseData()}/3/search/movie?&${generateMovieNames()}${getMovieCode()}&query=`; const searchTitle = document.getElementById("select-text"); const searchButton = document.getElementById("button-search"); @@ -62,7 +61,7 @@ async function fetchGenreMap() { localStorage.setItem('genreMap', JSON.stringify(genreMap)); } catch (error) { - console.error('Error fetching genre map:', error); + console.log('Error fetching genre map:', error); } } @@ -94,7 +93,7 @@ async function rotateUserStats() { { label: "Favorite Movies", getValue: () => { - const favoritedMovies = JSON.parse(localStorage.getItem('favoritesMovies')) || []; + const favoritedMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; return favoritedMovies.length; } }, @@ -102,8 +101,37 @@ async function rotateUserStats() { label: "Favorite Genre", getValue: () => { const mostCommonGenreCode = getMostCommonGenre(); - const genreMap = JSON.parse(localStorage.getItem('genreMap')) || {}; - return genreMap[mostCommonGenreCode] || 'Not Available'; + const genreMapString = localStorage.getItem('genreMap'); + if (!genreMapString) { + 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'; + } + + let genreObject; + if (Array.isArray(genreMap)) { + genreObject = genreMap.reduce((acc, genre) => { + acc[genre.id] = genre.name; + return acc; + }, {}); + } + 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'; + } + + return genreObject[mostCommonGenreCode] || 'Not Available'; } }, { label: "Watchlists Created", getValue: () => localStorage.getItem('watchlistsCreated') || 0 }, @@ -312,15 +340,25 @@ function showMovies(movies, mainElement) { : `
Image Not Available
`; const voteAvg = movie.vote_average.toFixed(1); const ratingClass = getClassByRate(movie.vote_average); + let title = movie.title; + const words = title.split(' '); + if (words.length >= 9) { + words[8] = '...'; + title = words.slice(0, 9).join(' '); + } + let overview = movie.overview; + if (overview === '') { + overview = 'No overview available.'; + } movieEl.innerHTML = ` ${movieImage}
-

${movie.title}

+

${title}

${voteAvg}

Overview:

- ${movie.overview} + ${overview}
`; movieEl.addEventListener('click', () => { localStorage.setItem('selectedMovieId', movie.id); @@ -412,7 +450,7 @@ async function showMovieOfTheDay() { } } catch (error) { - console.error('Error fetching movie:', error); + console.log('Error fetching movie:', error); fallbackMovieSelection(); } } diff --git a/MovieVerse-Mobile/app/js/notifications.js b/MovieVerse-Mobile/app/js/notifications.js new file mode 100644 index 00000000..39223f01 --- /dev/null +++ b/MovieVerse-Mobile/app/js/notifications.js @@ -0,0 +1,177 @@ +const movieCode = { + part1: 'YzVhMjBjODY=', + part2: 'MWFjZjdiYjg=', + part3: 'ZDllOTg3ZGNjN2YxYjU1OA==' +}; + +function getMovieCode() { + return atob(movieCode.part1) + atob(movieCode.part2) + atob(movieCode.part3); +} + +document.addEventListener('DOMContentLoaded', () => { + const today = new Date(); + fetchReleasesByCategory('releasesSinceLastVisit', new Date(localStorage.getItem('lastVisit')), today, true); + fetchReleasesByCategory('releasesThisMonth', new Date(today.getFullYear(), today.getMonth(), 1), today, false); + fetchReleasesByCategory('releasesThisYear', new Date(today.getFullYear(), 0, 1), today, false); + fetchRecommendedReleases(); +}); + +async function fetchReleasesByCategory(elementId, startDate, endDate, isLastVisit) { + const list = document.getElementById(elementId); + list.innerHTML = ''; + + let movies = await fetchMovies(startDate, endDate); + + movies = movies.sort((a, b) => new Date(b.release_date) - new Date(a.release_date)); + + populateList(elementId, movies.slice(0, 5)); +} + +async function fetchMovies(startDate, endDate) { + const formattedStartDate = `${startDate.getFullYear()}-${(startDate.getMonth() + 1).toString().padStart(2, '0')}-${startDate.getDate().toString().padStart(2, '0')}`; + const formattedEndDate = `${endDate.getFullYear()}-${(endDate.getMonth() + 1).toString().padStart(2, '0')}-${endDate.getDate().toString().padStart(2, '0')}`; + + const url = `https://${getMovieVerseData()}/3/discover/movie?${generateMovieNames()}${getMovieCode()}&release_date.gte=${formattedStartDate}&release_date.lte=${formattedEndDate}`; + + try { + const response = await fetch(url); + const data = await response.json(); + return data.results; + } + catch (error) { + console.log('Failed to fetch movies for', elementId + ':', error); + return []; + } +} + +function generateMovieNames(input) { + return String.fromCharCode(97, 112, 105, 95, 107, 101, 121, 61); +} + +async function getMostVisitedMovieGenre() { + const movieVisits = JSON.parse(localStorage.getItem('movieVisits')) || {}; + let mostVisitedGenre = null; + let maxVisits = 0; + for (const movieId in movieVisits) { + const visits = movieVisits[movieId]; + if (visits.count > maxVisits) { + maxVisits = visits.count; + mostVisitedGenre = await fetchGenreForMovie(movieId); + } + } + return mostVisitedGenre; +} + +async function fetchGenreForMovie(movieId) { + const movieDetailsUrl = `https://${getMovieVerseData()}/3/movie/${movieId}?${generateMovieNames()}${getMovieCode()}`; + const response = await fetch(movieDetailsUrl); + const movieDetails = await response.json(); + return movieDetails.genres[0] ? movieDetails.genres[0].id : null; +} + +async function fetchRecommendedReleases() { + let url; + + const mostCommonGenre = getMostCommonGenre(); + const mostVisitedMovieGenre = await getMostVisitedMovieGenre(); + + try { + const genreId = mostVisitedMovieGenre || mostCommonGenre; + if (!genreId) { + throw new Error('Genre ID is not valid.'); + } + url = `https://${getMovieVerseData()}/3/discover/movie?${generateMovieNames()}${getMovieCode()}&with_genres=${genreId}`; + } + catch (error) { + console.log('Fetching recommended movies failed or data issues:', error); + url = `https://${getMovieVerseData()}/3/movie/popular?${generateMovieNames()}${getMovieCode()}&language=en-US&page=1`; + } + + try { + const response = await fetch(url); + const data = await response.json(); + populateList('recommendedReleases', data.results.slice(0, 5)); + } + catch (error) { + console.log('Failed to fetch movies:', error); + } +} + +function populateList(elementId, movies) { + const list = document.getElementById(elementId); + list.innerHTML = ''; + movies.forEach(movie => { + const li = document.createElement('li'); + li.style.cursor = 'pointer'; + li.addEventListener('click', () => { + localStorage.setItem('selectedMovieId', movie.id.toString()); + window.location.href = 'movie-details.html'; + }); + + const title = document.createElement('span'); + title.textContent = movie.title; + title.style.color = 'black'; + li.appendChild(title); + list.appendChild(li); + }); +} + +document.addEventListener('DOMContentLoaded', () => { + populateActors(); + populateDirectors(); +}); + +function populateActors() { + const actors = [ + { name: "Robert Downey Jr.", id: 3223 }, + { name: "Scarlett Johansson", id: 1245 }, + { name: "Denzel Washington", id: 5292 }, + { name: "Meryl Streep", id: 5064 }, + { name: "Leonardo DiCaprio", id: 6193 }, + { name: "Sandra Bullock", id: 18277 }, + { name: "Tom Hanks", id: 31 } + ]; + + const list = document.getElementById('popularActors').querySelector('ul'); + actors.forEach(actor => { + const li = document.createElement('li'); + li.style.cursor = 'pointer'; + li.addEventListener('click', () => { + localStorage.setItem('selectedActorId', actor.id.toString()); + window.location.href = 'actor-details.html'; + }); + + const name = document.createElement('span'); + name.textContent = actor.name; + li.appendChild(name); + list.appendChild(li); + }); +} + +function populateDirectors() { + const directors = [ + {name: "Steven Spielberg", id: 488}, + {name: "Martin Scorsese", id: 1032}, + {name: "Christopher Nolan", id: 525}, + {name: "Quentin Tarantino", id: 138}, + {name: "Kathryn Bigelow", id: 14392}, + {name: "James Cameron", id: 2710}, + {name: "Sofia Coppola", id: 1776} + ]; + + const list = document.getElementById('popularDirectors').querySelector('ul'); + directors.forEach(director => { + const li = document.createElement('li'); + li.style.cursor = 'pointer'; + li.addEventListener('click', () => { + localStorage.setItem('selectedDirectorId', director.id.toString()); + window.location.href = 'director-details.html'; + }); + + const name = document.createElement('span'); + name.textContent = director.name; + li.appendChild(name); + + list.appendChild(li); + }); +} diff --git a/MovieVerse-Mobile/app/js/quiz.js b/MovieVerse-Mobile/app/js/quiz.js index c949169e..a674b85e 100644 --- a/MovieVerse-Mobile/app/js/quiz.js +++ b/MovieVerse-Mobile/app/js/quiz.js @@ -1,3 +1,5 @@ +import { updateTriviaStats } from './triviaModule.js'; + const questionBank = [ { question: "What movie won the Academy Award for Best Picture in 2020?", options: ["Joker", "1917", "Parasite"], answer: "Parasite" }, { question: "Who directed the movie 'The Godfather'?", options: ["Steven Spielberg", "Francis Ford Coppola", "Martin Scorsese"], answer: "Francis Ford Coppola" }, @@ -71,6 +73,61 @@ const questionBank = [ { question: "Which movie did Leonardo DiCaprio win his first Oscar for Best Actor?", options: ["The Revenant", "The Wolf of Wall Street", "Inception"], answer: "The Revenant" }, { question: "In which film does the character Maximus Decimus Meridius appear?", options: ["300", "Gladiator", "Troy"], answer: "Gladiator" }, { question: "What is the name of the fictional British spy in the film series created by Ian Fleming?", options: ["James Bond", "Jason Bourne", "Jack Ryan"], answer: "James Bond" }, + { question: "Which movie won the Academy Award for Best Animated Feature in 2021?", options: ["Onward", "Soul", "Wolfwalkers"], answer: "Soul" }, + { question: "Who played the role of Michael Corleone in 'The Godfather'?", options: ["Al Pacino", "Robert De Niro", "Marlon Brando"], answer: "Al Pacino" }, + { question: "What 2009 film is known for pioneering modern 3D cinema technology?", options: ["Inception", "Avatar", "The Hurt Locker"], answer: "Avatar" }, + { question: "Which 2012 film features a protagonist who survives a shipwreck with a tiger?", options: ["Life of Pi", "Cast Away", "The Revenant"], answer: "Life of Pi" }, + { question: "What is the main theme of the movie 'Inception'?", options: ["Time travel", "Dream manipulation", "Space exploration"], answer: "Dream manipulation" }, + { question: "Which film features the character Sarah Connor, who is central to the plot?", options: ["The Terminator", "Aliens", "Jurassic Park"], answer: "The Terminator" }, + { question: "What 1999 movie is famous for the quote, 'I see dead people'?", options: ["The Sixth Sense", "Ghost", "The Others"], answer: "The Sixth Sense" }, + { question: "Who directed 'Titanic', which won the Academy Award for Best Picture in 1997?", options: ["James Cameron", "Steven Spielberg", "Martin Scorsese"], answer: "James Cameron" }, + { question: "Which film did NOT feature Leonardo DiCaprio?", options: ["Titanic", "The Great Gatsby", "The Prestige"], answer: "The Prestige" }, + { question: "In which movie do characters compete in the 'Hunger Games'?", options: ["Catching Fire", "The Hunger Games", "Battle Royale"], answer: "The Hunger Games" }, + { question: "What film, released in 1982, features a character named E.T.?", options: ["Star Wars", "Close Encounters of the Third Kind", "E.T. the Extra-Terrestrial"], answer: "E.T. the Extra-Terrestrial" }, + { question: "Who starred as the lead in the 2018 film 'Black Panther'?", options: ["Chadwick Boseman", "Michael B. Jordan", "Denzel Washington"], answer: "Chadwick Boseman" }, + { question: "What iconic 1980s movie features the quote, 'Say hello to my little friend!'?", options: ["Scarface", "The Godfather", "Goodfellas"], answer: "Scarface" }, + { question: "Which film features a unique spinning top in its final scene?", options: ["Inception", "Minority Report", "The Matrix"], answer: "Inception" }, + { question: "What movie, featuring a journey to Mordor, won the Academy Award for Best Picture in 2003?", options: ["The Lord of the Rings: The Two Towers", "The Lord of the Rings: The Return of the King", "The Lord of the Rings: The Fellowship of the Ring"], answer: "The Lord of the Rings: The Return of the King" }, + { question: "Which movie features a giant monster known as Godzilla?", options: ["Pacific Rim", "Godzilla", "Cloverfield"], answer: "Godzilla" }, + { question: "What classic film was remade in 2005 starring Naomi Watts and Jack Black?", options: ["King Kong", "Godzilla", "Planet of the Apes"], answer: "King Kong" }, + { question: "Who directed the 1994 crime film 'Pulp Fiction'?", options: ["Quentin Tarantino", "Steven Spielberg", "Martin Scorsese"], answer: "Quentin Tarantino" }, + { question: "Which movie includes a character named Norman Bates?", options: ["Psycho", "Rebecca", "The Birds"], answer: "Psycho" }, + { question: "What is the name of the fictional theme park in 'Jurassic Park'?", options: ["Dinosaur Land", "Jurassic World", "Isla Nublar"], answer: "Isla Nublar" }, + { question: "Who played the role of Clarice Starling in the film 'The Silence of the Lambs'?", options: ["Jodie Foster", "Julianne Moore", "Sigourney Weaver"], answer: "Jodie Foster" }, + { question: "Which film is famous for the line, 'May the Force be with you'?", options: ["Star Trek", "Star Wars", "Guardians of the Galaxy"], answer: "Star Wars" }, + { question: "What 1975 thriller is known for its menacing shark and famous soundtrack?", options: ["Deep Blue Sea", "Jaws", "Sharknado"], answer: "Jaws" }, + { question: "Which film did Tom Hanks win his first Academy Award for Best Actor?", options: ["Big", "Philadelphia", "Forrest Gump"], answer: "Philadelphia" }, + { question: "What is the name of the ring in 'The Lord of the Rings'?", options: ["The Ring of Power", "The One Ring", "The Master Ring"], answer: "The One Ring" }, + { question: "Who directed 'Avatar', the groundbreaking sci-fi movie released in 2009?", options: ["James Cameron", "George Lucas", "Steven Spielberg"], answer: "James Cameron" }, + { question: "Which 1988 animated film features a dystopian future and psychic powers?", options: ["Ghost in the Shell", "Akira", "Blade Runner"], answer: "Akira" }, + { question: "Who played the role of Hermione Granger in the Harry Potter films?", options: ["Emma Watson", "Emma Stone", "Emily Blunt"], answer: "Emma Watson" }, + { question: "Which film features a group of friends who use a map to find a pirate's treasure?", options: ["The Goonies", "Treasure Island", "Pirates of the Caribbean"], answer: "The Goonies" }, + { question: "What was the first animated film to receive a Best Picture nomination at the Oscars?", options: ["Beauty and the Beast", "The Lion King", "Up"], answer: "Beauty and the Beast" }, + { question: "What is the fictional sport played in the 'Harry Potter' series?", options: ["Quidditch", "Bludgers", "Snitchball"], answer: "Quidditch" }, + { question: "Who composed the iconic score for 'Star Wars'?", options: ["Hans Zimmer", "John Williams", "Danny Elfman"], answer: "John Williams" }, + { question: "What 2000 film, directed by Ridley Scott, features a Roman general turned gladiator?", options: ["Spartacus", "Gladiator", "Ben-Hur"], answer: "Gladiator" }, + { question: "Which movie's plot centers around a unique wooden board game?", options: ["Clue", "Jumanji", "Zathura"], answer: "Jumanji" }, + { question: "Who directed the 1980 horror film 'The Shining'?", options: ["Stanley Kubrick", "Alfred Hitchcock", "Stephen King"], answer: "Stanley Kubrick" }, + { question: "What 1993 science fiction film directed by Steven Spielberg features dinosaurs brought back to life through cloning?", options: ["Jurassic Park", "The Lost World", "Dinosaur"], answer: "Jurassic Park" }, + { question: "Who voiced the character of Woody in the 'Toy Story' movies?", options: ["Tom Hanks", "Tim Allen", "Billy Crystal"], answer: "Tom Hanks" }, + { question: "Which 2010 film directed by Christopher Nolan explores dream-sharing technology?", options: ["Inception", "Interstellar", "Memento"], answer: "Inception" }, + { question: "What film series features a secret British spy agency known as Kingsman?", options: ["James Bond", "Kingsman", "Johnny English"], answer: "Kingsman" }, + { question: "Who played the role of Jack Sparrow in the 'Pirates of the Caribbean' film series?", options: ["Johnny Depp", "Orlando Bloom", "Keira Knightley"], answer: "Johnny Depp" }, + { question: "Which 2001 film, based on a J.R.R. Tolkien novel, follows a hobbit's quest to destroy a powerful ring?", options: ["The Hobbit", "The Lord of the Rings: The Fellowship of the Ring", "The Lord of the Rings: The Two Towers"], answer: "The Lord of the Rings: The Fellowship of the Ring" }, + { question: "What 2003 animated film features a fish named Nemo?", options: ["Shark Tale", "Finding Nemo", "The Little Mermaid"], answer: "Finding Nemo" }, + { question: "Which 2017 film is based on a DC Comics character and set during World War I?", options: ["Wonder Woman", "Captain America: The First Avenger", "Justice League"], answer: "Wonder Woman" }, + { question: "Who directed the 1994 film 'Pulp Fiction'?", options: ["Quentin Tarantino", "Martin Scorsese", "Ridley Scott"], answer: "Quentin Tarantino" }, + { question: "What movie introduced the character of Hannibal Lecter?", options: ["Silence of the Lambs", "Hannibal", "Manhunter"], answer: "Manhunter" }, + { question: "Which 2016 film tells the story of a group of rebels who plan to steal plans for the Death Star?", options: ["Star Wars: The Force Awakens", "Rogue One: A Star Wars Story", "Star Wars: The Last Jedi"], answer: "Rogue One: A Star Wars Story" }, + { question: "What is the name of the fictional African kingdom in 'Coming to America'?", options: ["Wakanda", "Zamunda", "Genovia"], answer: "Zamunda" }, + { question: "Who directed the 2017 movie 'Get Out'?", options: ["Jordan Peele", "Spike Lee", "John Singleton"], answer: "Jordan Peele" }, + { question: "Which movie features an AI character named HAL 9000?", options: ["Blade Runner", "Ex Machina", "2001: A Space Odyssey"], answer: "2001: A Space Odyssey" }, + { question: "What 1980s movie is known for the quote 'Nobody puts Baby in a corner'?", options: ["Dirty Dancing", "Footloose", "Flashdance"], answer: "Dirty Dancing" }, + { question: "What 1995 film directed by Michael Mann stars Robert De Niro and Al Pacino?", options: ["Heat", "The Godfather", "Scarface"], answer: "Heat" }, + { question: "Who starred as the titular character in the 2014 film 'Maleficent'?", options: ["Angelina Jolie", "Charlize Theron", "Nicole Kidman"], answer: "Angelina Jolie" }, + { question: "Which film is about a board game that becomes real for the players?", options: ["Zathura", "Jumanji", "The Game"], answer: "Jumanji" }, + { question: "In which movie does a group of archaeologists find a frozen prehistoric man?", options: ["Encino Man", "Ice Age", "The Thing"], answer: "Encino Man" }, + { question: "What movie features a theme park filled with cloned dinosaurs?", options: ["Jurassic Park", "Westworld", "Prehistoric Park"], answer: "Jurassic Park" } ]; const movieCode = { @@ -87,25 +144,6 @@ function generateMovieNames(input) { return String.fromCharCode(97, 112, 105, 95, 107, 101, 121, 61); } -const IMGPATH = "https://image.tmdb.org/t/p/w1280"; -const main = document.getElementById("main"); -const search = document.getElementById("search"); -const searchButton = document.getElementById("button-search"); -const SEARCHPATH = `https://${getMovieVerseData()}/3/search/movie?&${generateMovieNames()}${getMovieCode()}&query=`; -const searchTitle = document.getElementById("trivia-label"); - -function getClassByRate(vote){ - if (vote >= 8) { - return 'green'; - } - else if (vote >= 5) { - return 'orange'; - } - else { - return 'red'; - } -} - const form = document.getElementById("form"); form.addEventListener('submit', (e) => { @@ -121,118 +159,6 @@ function handleSearch() { window.location.href = 'search.html'; } -async function getMovies(url) { - clearMovieDetails(); - const numberOfMovies = calculateMoviesToDisplay(); - const pagesToFetch = numberOfMovies <= 20 ? 1 : 2; - let allMovies = []; - - for (let page = 1; page <= pagesToFetch; page++) { - const response = await fetch(`${url}&page=${page}`); - const data = await response.json(); - allMovies = allMovies.concat(data.results); - } - - const popularityThreshold = 0.5; - - allMovies.sort((a, b) => { - const popularityDifference = Math.abs(a.popularity - b.popularity); - if (popularityDifference < popularityThreshold) { - return b.vote_average - a.vote_average; - } - return b.popularity - a.popularity; - }); - - document.getElementById('clear-search-btn').style.display = 'block'; - - if (allMovies.length > 0) { - showMovies(allMovies.slice(0, numberOfMovies)); - } - else { - main.innerHTML = `

No movie with the specified search term found. Please try again.

`; - document.getElementById('clear-search-btn').style.display = 'none'; - } -} - -document.getElementById('clear-search-btn').addEventListener('click', () => { - location.reload(); -}); - -function clearMovieDetails() { - const movieDetailsContainer = document.getElementById('quiz-container'); - if (movieDetailsContainer) { - movieDetailsContainer.innerHTML = ''; - } - document.getElementById('regenerate-questions').style.display = 'none'; - document.getElementById('submit').style.display = 'none'; -} - -function showMovies(movies){ - main.innerHTML = ''; - movies.forEach((movie) => { - const { id, poster_path, title, vote_average, overview } = movie; - const movieE1 = document.createElement('div'); - const voteAverage = vote_average.toFixed(1); - movieE1.classList.add('movie'); - - const movieImage = poster_path - ? `${title}` - : `
Image Not Available
`; - - movieE1.innerHTML = ` - ${movieImage} -
-

${title}

- ${voteAverage} -
-
-

Movie Overview:

- ${overview} -
`; - - movieE1.addEventListener('click', () => { - localStorage.setItem('selectedMovieId', id); - window.location.href = 'movie-details.html'; - updateMovieVisitCount(id, title); - }); - - main.appendChild(movieE1); - }); -} - -function calculateMoviesToDisplay() { - const screenWidth = window.innerWidth; - if (screenWidth <= 689.9) return 10; - if (screenWidth <= 1021.24) return 20; - if (screenWidth <= 1353.74) return 21; - if (screenWidth <= 1684.9) return 20; - if (screenWidth <= 2017.49) return 20; - if (screenWidth <= 2349.99) return 18; - if (screenWidth <= 2681.99) return 21; - if (screenWidth <= 3014.49) return 24; - if (screenWidth <= 3345.99) return 27; - if (screenWidth <= 3677.99) return 20; - if (screenWidth <= 4009.99) return 22; - if (screenWidth <= 4340.99) return 24; - if (screenWidth <= 4673.49) return 26; - if (screenWidth <= 5005.99) return 28; - if (screenWidth <= 5337.99) return 30; - if (screenWidth <= 5669.99) return 32; - if (screenWidth <= 6001.99) return 34; - if (screenWidth <= 6333.99) return 36; - if (screenWidth <= 6665.99) return 38; - if (screenWidth <= 6997.99) return 40; - if (screenWidth <= 7329.99) return 42; - if (screenWidth <= 7661.99) return 44; - if (screenWidth <= 7993.99) return 46; - if (screenWidth <= 8325.99) return 48; - return 20; -} - -document.addEventListener('DOMContentLoaded', () => { - document.getElementById('clear-search-btn').style.display = 'none'; -}); - function generateRandomQuestions() { const questionsToDisplay = 10; const shuffledQuestions = questionBank.sort(() => 0.5 - Math.random()); @@ -262,24 +188,6 @@ function generateRandomQuestions() { }); } -function updateTriviaStats(correctAnswers, totalQuestions) { - let triviaStats = JSON.parse(localStorage.getItem('triviaStats')) || { totalCorrect: 0, totalAttempted: 0 }; - - triviaStats.totalCorrect += correctAnswers; - triviaStats.totalAttempted += totalQuestions; - - localStorage.setItem('triviaStats', JSON.stringify(triviaStats)); -} - -function getTriviaAccuracy() { - let triviaStats = JSON.parse(localStorage.getItem('triviaStats')) || { totalCorrect: 0, totalAttempted: 0 }; - if (triviaStats.totalAttempted === 0) { - return 'No trivia attempted'; - } - let accuracy = (triviaStats.totalCorrect / triviaStats.totalAttempted) * 100; - return `${accuracy.toFixed(1)}% accuracy`; -} - document.getElementById('regenerate-questions').addEventListener('click', generateRandomQuestions); generateRandomQuestions(); @@ -302,7 +210,7 @@ async function showMovieOfTheDay() { } } catch (error) { - console.error('Error fetching movie:', error); + console.log('Error fetching movie:', error); fallbackMovieSelection(); } } @@ -389,7 +297,9 @@ function calculateAndDisplayResults() { } }); - updateTriviaStats(score, totalQuestions); + const currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser') || null; + + updateTriviaStats(currentUserEmail, score, totalQuestions); displayResults(score); } @@ -428,4 +338,4 @@ function showModal() { modal.style.display = "none"; } }); -} \ No newline at end of file +} diff --git a/MovieVerse-Mobile/app/js/ratings-module.js b/MovieVerse-Mobile/app/js/ratings-module.js new file mode 100644 index 00000000..37bd576f --- /dev/null +++ b/MovieVerse-Mobile/app/js/ratings-module.js @@ -0,0 +1,80 @@ +import { initializeApp } from "https://www.gstatic.com/firebasejs/10.8.0/firebase-app.js"; +import { getFirestore, doc, setDoc, getDoc } from "https://www.gstatic.com/firebasejs/10.8.0/firebase-firestore.js"; + +const firebaseConfig = { + apiKey: atob("QUl6YVN5REw2a1FuU2ZVZDhVdDhIRnJwS3VpdnF6MXhkWG03aw=="), + authDomain: atob("bW92aWV2ZXJzZS1hcHAuZmlyZWJhc2VhcHAuY29t"), + projectId: "movieverse-app", + storageBucket: atob("bW92aWV2ZXJzZS1hcHAuYXBwc3BvdC5jb20="), + messagingSenderId: atob("ODAyOTQzNzE4ODcx"), + appId: atob("MTo4MDI5NDM3MTg4NzE6d2ViOjQ4YmM5MTZjYzk5ZTI3MjQyMTI3OTI=") +}; + +const app = initializeApp(firebaseConfig); +const db = getFirestore(app); + +export async function loadUserRatings(currentUserEmail) { + if (currentUserEmail) { + const ratingsRef = doc(db, 'userRatings', currentUserEmail); + const docSnap = await getDoc(ratingsRef); + return docSnap.exists() ? docSnap.data().ratings : {}; + } + else { + return JSON.parse(localStorage.getItem('movieRatings')) || {}; + } +} + +export async function updateAverageMovieRating(currentUserEmail, movieId, newRating) { + if (!currentUserEmail) { + console.error("No user signed in, using localStorage to save ratings."); + const savedRatings = JSON.parse(localStorage.getItem('movieRatings')) || {}; + savedRatings[movieId] = newRating; + localStorage.setItem('movieRatings', JSON.stringify(savedRatings)); + updateLocalAverage(savedRatings); + } + else { + console.log("User signed in, saving ratings to Firebase."); + const ratingsRef = doc(db, 'userRatings', currentUserEmail); + const docSnap = await getDoc(ratingsRef); + let ratings = docSnap.exists() ? docSnap.data().ratings || {} : {}; + ratings[movieId] = newRating; + + await setDoc(ratingsRef, { ratings: ratings }, { merge: true }); + updateFirebaseAverage(ratings, ratingsRef); + updateLocalAverage(ratings); + } +} + +function updateLocalAverage(savedRatings) { + let totalRating = 0; + let totalMoviesRated = 0; + Object.values(savedRatings).forEach(rating => { + totalRating += parseFloat(rating); + totalMoviesRated++; + }); + const averageRating = totalMoviesRated > 0 ? (totalRating / totalMoviesRated) : 0; + localStorage.setItem('averageMovieRating', averageRating.toFixed(1)); +} + +async function updateFirebaseAverage(ratings, ratingsRef) { + let totalRating = 0; + let totalMoviesRated = 0; + Object.values(ratings).forEach(rating => { + totalRating += parseFloat(rating); + totalMoviesRated++; + }); + const averageRating = totalMoviesRated > 0 ? (totalRating / totalMoviesRated) : 0; + await setDoc(ratingsRef, { averageRating: averageRating }, { merge: true }); +} + +export async function getAverageMovieRating(currentUserEmail) { + if (!currentUserEmail) { + console.error("No user signed in, retrieving average rating from localStorage."); + return localStorage.getItem('averageMovieRating') || 0; + } + else { + const ratingsRef = doc(db, 'userRatings', currentUserEmail); + const docSnap = await getDoc(ratingsRef); + return docSnap.exists() && docSnap.data().averageRating ? docSnap.data().averageRating : 0; + } +} diff --git a/MovieVerse-Mobile/app/js/reset-password.js b/MovieVerse-Mobile/app/js/reset-password.js index 059e28ce..d167c366 100644 --- a/MovieVerse-Mobile/app/js/reset-password.js +++ b/MovieVerse-Mobile/app/js/reset-password.js @@ -43,18 +43,31 @@ const app = initializeApp(firebaseConfig); const db = getFirestore(app); document.getElementById('resetPasswordForm').addEventListener('submit', async function(event) { - event.preventDefault(); - const resetEmail = document.getElementById('resetEmail').value; + try { + event.preventDefault(); + const resetEmail = document.getElementById('resetEmail').value; - const q = query(collection(db, "MovieVerseUsers"), where("email", "==", resetEmail)); - const querySnapshot = await getDocs(q); + const q = query(collection(db, "MovieVerseUsers"), where("email", "==", resetEmail)); + const querySnapshot = await getDocs(q); - if (querySnapshot.empty) { - alert("No account with such credentials exists in our database, or you might have mistyped something. Please try again."); - return; - } + if (querySnapshot.empty) { + alert("No account with such credentials exists in our database, or you might have mistyped something. Please try again."); + return; + } - document.getElementById('newPasswordFields').style.display = 'block'; + document.getElementById('newPasswordFields').style.display = 'block'; + } + catch (error) { + console.error("Error fetching user list: ", error); + if (error.code === 'resource-exhausted') { + const noUserSelected = document.getElementById('password-reset-form-container'); + if (noUserSelected) { + noUserSelected.innerHTML = "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!"; + noUserSelected.style.height = '350px'; + } + hideSpinner(); + } + } }); async function updatePassword() { @@ -89,7 +102,7 @@ async function updatePassword() { alert("Password updated successfully!"); window.location.href = 'sign-in.html'; }).catch((error) => { - console.error("Error updating password: ", error); + console.log("Error updating password: ", error); alert("Failed to update password. Please try again."); }); }); diff --git a/MovieVerse-Mobile/app/js/search.js b/MovieVerse-Mobile/app/js/search.js index 4f37b3b3..c4f9c0b1 100644 --- a/MovieVerse-Mobile/app/js/search.js +++ b/MovieVerse-Mobile/app/js/search.js @@ -1,5 +1,5 @@ const form = document.getElementById('form1'); -const IMGPATH = "https://image.tmdb.org/t/p/w1280"; +const IMGPATH = "https://image.tmdb.org/t/p/w500"; function showSpinner() { document.getElementById('myModal').classList.add('modal-visible'); @@ -27,22 +27,128 @@ async function ensureGenreMapIsAvailable() { } } +document.addEventListener('DOMContentLoaded', () => { + showResults('movie'); + updateCategoryButtonStyles('movie'); + attachEventListeners(); + attachArrowKeyNavigation(); + fetchGenreMap(); + fetchTvGenreMap(); + fetchLanguages(); + fetchTvLanguages(); + + document.getElementById('form1').addEventListener('submit', function(event) { + event.preventDefault(); + handleSearch(); + }); +}); + +async function fetchTvLanguages() { + const url = `https://${getMovieVerseData()}/3/configuration/languages?${generateMovieNames()}${getMovieCode()}`; + + try { + const response = await fetch(url); + let languages = await response.json(); + languages = languages.sort((a, b) => a.english_name.localeCompare(b.english_name)); + populateTvLanguageFilter(languages); + } + catch (error) { + console.log('Error fetching languages:', error); + } +} + +function populateTvLanguageFilter(languages) { + const languageFilter = document.getElementById('language-tv-filter'); + languageFilter.innerHTML = ''; + + languages.forEach(language => { + const option = document.createElement('option'); + option.value = language.iso_639_1; + option.textContent = language.english_name; + languageFilter.appendChild(option); + }); +} + +async function fetchLanguages() { + const url = `https://${getMovieVerseData()}/3/configuration/languages?${generateMovieNames()}${getMovieCode()}`; + + try { + const response = await fetch(url); + let languages = await response.json(); + languages = languages.sort((a, b) => a.english_name.localeCompare(b.english_name)); + populateLanguageFilter(languages); + } + catch (error) { + console.log('Error fetching languages:', error); + } +} + +function populateLanguageFilter(languages) { + const languageFilter = document.getElementById('language-filter'); + languageFilter.innerHTML = ''; + + languages.forEach(language => { + const option = document.createElement('option'); + option.value = language.iso_639_1; + option.textContent = language.english_name; + languageFilter.appendChild(option); + }); +} + async function fetchGenreMap() { - const url = `https://${getMovieVerseData()}/3/genre/movie/list?${generateMovieNames()}${getMovieCode()}`; + const code = getMovieCode(); + const url = `https://${getMovieVerseData()}/3/genre/movie/list?${generateMovieNames()}${code}`; + + try { + const response = await fetch(url); + const data = await response.json(); + localStorage.setItem('genreMap', JSON.stringify(data.genres)); + populateGenreFilter(data.genres); + } + catch (error) { + console.log('Error fetching genre map:', error); + } +} + +async function fetchTvGenreMap() { + const code = getMovieCode(); + const url = `https://${getMovieVerseData()}/3/genre/tv/list?${generateMovieNames()}${code}`; + try { const response = await fetch(url); const data = await response.json(); - const genreMap = data.genres.reduce((map, genre) => { - map[genre.id] = genre.name; - return map; - }, {}); - localStorage.setItem('genreMap', JSON.stringify(genreMap)); + localStorage.setItem('tvGenreMap', JSON.stringify(data.genres)); + populateTvGenreFilter(data.genres); } catch (error) { - console.error('Error fetching genre map:', error); + console.log('Error fetching TV genre map:', error); } } +function populateGenreFilter(genres) { + const genreFilter = document.getElementById('genre-filter'); + genreFilter.innerHTML = ''; + + genres.forEach(genre => { + const option = document.createElement('option'); + option.value = genre.id; + option.textContent = genre.name; + genreFilter.appendChild(option); + }); +} + +function populateTvGenreFilter(genres) { + const genreFilter = document.getElementById('genre-tv-filter'); + genreFilter.innerHTML = ''; + + genres.forEach(genre => { + const option = document.createElement('option'); + option.value = genre.id; + option.textContent = genre.name; + genreFilter.appendChild(option); + }); +} + async function rotateUserStats() { await ensureGenreMapIsAvailable(); @@ -71,7 +177,7 @@ async function rotateUserStats() { { label: "Favorite Movies", getValue: () => { - const favoritedMovies = JSON.parse(localStorage.getItem('favoritesMovies')) || []; + const favoritedMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; return favoritedMovies.length; } }, @@ -79,8 +185,37 @@ async function rotateUserStats() { label: "Favorite Genre", getValue: () => { const mostCommonGenreCode = getMostCommonGenre(); - const genreMap = JSON.parse(localStorage.getItem('genreMap')) || {}; - return genreMap[mostCommonGenreCode] || 'Not Available'; + const genreMapString = localStorage.getItem('genreMap'); + if (!genreMapString) { + 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'; + } + + let genreObject; + if (Array.isArray(genreMap)) { + genreObject = genreMap.reduce((acc, genre) => { + acc[genre.id] = genre.name; + return acc; + }, {}); + } + 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'; + } + + return genreObject[mostCommonGenreCode] || 'Not Available'; } }, { label: "Watchlists Created", getValue: () => localStorage.getItem('watchlistsCreated') || 0 }, @@ -125,11 +260,19 @@ async function rotateUserStats() { function updateMovieVisitCount(movieId, movieTitle) { let movieVisits = JSON.parse(localStorage.getItem('movieVisits')) || {}; + let uniqueMoviesViewed = JSON.parse(localStorage.getItem('uniqueMoviesViewed')) || []; + if (!movieVisits[movieId]) { movieVisits[movieId] = { count: 0, title: movieTitle }; } movieVisits[movieId].count += 1; + + if (!uniqueMoviesViewed.includes(movieId)) { + uniqueMoviesViewed.push(movieId); + } + localStorage.setItem('movieVisits', JSON.stringify(movieVisits)); + localStorage.setItem('uniqueMoviesViewed', JSON.stringify(uniqueMoviesViewed)); } function getMostVisitedMovie() { @@ -179,6 +322,7 @@ function getMostVisitedDirector() { function getTriviaAccuracy() { let triviaStats = JSON.parse(localStorage.getItem('triviaStats')) || { totalCorrect: 0, totalAttempted: 0 }; + if (triviaStats.totalAttempted === 0) { return 'No trivia attempted'; } @@ -213,23 +357,189 @@ function attachEventListeners() { const movieBtn = document.querySelector('[data-category="movie"]'); const tvBtn = document.querySelector('[data-category="tv"]'); const peopleBtn = document.querySelector('[data-category="person"]'); + const toggleFiltersBtn = document.getElementById('toggle-filters-btn'); + + const movieFilters = document.getElementById('movie-tv-filters'); + const tvFilters = document.getElementById('tv-filters'); + const peopleFilters = document.getElementById('people-filters'); + + const genreMovieFilter = document.getElementById('genre-filter'); + const yearMovieFilter = document.getElementById('year-filter'); + const ratingMovieFilter = document.getElementById('rating-filter'); + const languageFilter = document.getElementById('language-filter'); + + const genreTvFilter = document.getElementById('genre-tv-filter'); + const yearTvFilter = document.getElementById('year-tv-filter'); + const ratingTvFilter = document.getElementById('rating-tv-filter'); + const languageTvFilter = document.getElementById('language-tv-filter'); + + const professionFilter = document.getElementById('profession-filter'); + const genderFilter = document.getElementById('gender-filter'); + const popularityFilter = document.getElementById('popularity-filter'); + + const ratingValueSpan = document.getElementById('rating-value'); + const ratingTvValueSpan = document.getElementById('rating-tv-value'); + const popularityValueSpan = document.getElementById('popularity-value'); + + movieFilters.style.display = 'none'; + tvFilters.style.display = 'none'; + peopleFilters.style.display = 'none'; + + function setFilterDisplayValues() { + ratingValueSpan.textContent = `Rating: ${ratingMovieFilter.value} and above`; + ratingTvValueSpan.textContent = `Rating: ${ratingTvFilter.value} and above`; + popularityValueSpan.textContent = `Popularity: ${popularityFilter.value} and above`; + } + + function showCorrectFilters(category) { + movieFilters.style.display = category === 'movie' ? 'block' : 'none'; + tvFilters.style.display = category === 'tv' ? 'block' : 'none'; + peopleFilters.style.display = category === 'person' ? 'block' : 'none'; + } - movieBtn.addEventListener('click', function() { + movieBtn.addEventListener('click', () => { showResults('movie'); updateCategoryButtonStyles('movie'); + showCorrectFilters('movie'); + movieFilters.style.display = 'none'; + tvFilters.style.display = 'none'; + peopleFilters.style.display = 'none'; + toggleFiltersBtn.textContent = 'Filter Results'; }); - tvBtn.addEventListener('click', function() { + tvBtn.addEventListener('click', () => { showResults('tv'); updateCategoryButtonStyles('tv'); + showCorrectFilters('tv'); + movieFilters.style.display = 'none'; + tvFilters.style.display = 'none'; + peopleFilters.style.display = 'none'; + toggleFiltersBtn.textContent = 'Filter Results'; }); - peopleBtn.addEventListener('click', function() { + peopleBtn.addEventListener('click', () => { showResults('person'); updateCategoryButtonStyles('person'); + showCorrectFilters('person'); + movieFilters.style.display = 'none'; + tvFilters.style.display = 'none'; + peopleFilters.style.display = 'none'; + toggleFiltersBtn.textContent = 'Filter Results'; }); + + toggleFiltersBtn.addEventListener('click', () => { + if (currentCategory === 'movie') { + movieFilters.style.display = movieFilters.style.display === 'none' ? 'block' : 'none'; + } + else if (currentCategory === 'tv') { + tvFilters.style.display = tvFilters.style.display === 'none' ? 'block' : 'none'; + } + else if (currentCategory === 'person') { + peopleFilters.style.display = peopleFilters.style.display === 'none' ? 'block' : 'none'; + } + }); + + genreMovieFilter.addEventListener('change', () => showResults('movie')); + yearMovieFilter.addEventListener('change', () => showResults('movie')); + ratingMovieFilter.addEventListener('input', () => { + ratingValueSpan.textContent = `Rating: ${ratingMovieFilter.value} and above`; + showResults('movie'); + }); + languageFilter.addEventListener('change', () => showResults('movie')); + + genreTvFilter.addEventListener('change', () => showResults('tv')); + yearTvFilter.addEventListener('change', () => showResults('tv')); + ratingTvFilter.addEventListener('input', () => { + ratingTvValueSpan.textContent = `Rating: ${ratingTvFilter.value} and above`; + showResults('tv'); + }); + languageTvFilter.addEventListener('change', () => showResults('tv')); + + genderFilter.addEventListener('change', () => showResults('person')); + professionFilter.addEventListener('change', () => showResults('person')); + popularityFilter.addEventListener('input', () => { + popularityValueSpan.textContent = `Popularity: ${popularityFilter.value} and above`; + showResults('person'); + }); + + const resetMovieFiltersBtn = movieFilters.querySelector('button[id="reset-filters"]'); + const resetTvFiltersBtn = tvFilters.querySelector('button[id="reset-filters"]'); + const resetPeopleFiltersBtn = peopleFilters.querySelector('button[id="reset-filters"]'); + + resetMovieFiltersBtn.addEventListener('click', () => { + genreMovieFilter.selectedIndex = 0; + yearMovieFilter.value = ''; + ratingMovieFilter.value = 5; + languageFilter.selectedIndex = 0; + setFilterDisplayValues(); + showResults('movie'); + }); + + resetTvFiltersBtn.addEventListener('click', () => { + genreTvFilter.selectedIndex = 0; + yearTvFilter.value = ''; + ratingTvFilter.value = 5; + languageTvFilter.selectedIndex = 0; + setFilterDisplayValues(); + showResults('tv'); + }); + + resetPeopleFiltersBtn.addEventListener('click', () => { + professionFilter.selectedIndex = 0; + genderFilter.selectedIndex = 0; + popularityFilter.value = 20; + setFilterDisplayValues(); + showResults('person'); + }); + + setFilterDisplayValues(); + showCorrectFilters(localStorage.getItem('selectedCategory')); } +let currentCategory = 'movie'; + +document.addEventListener('DOMContentLoaded', function() { + const toggleFiltersBtn = document.getElementById('toggle-filters-btn'); + const movieTvFilters = document.getElementById('movie-tv-filters'); + const peopleFilters = document.getElementById('people-filters'); + const tvFilters = document.getElementById('tv-filters'); + + movieTvFilters.style.display = 'none'; + peopleFilters.style.display = 'none'; + tvFilters.style.display = 'none'; + + toggleFiltersBtn.addEventListener('click', function() { + if (currentCategory === 'movie') { + movieTvFilters.style.display = movieTvFilters.style.display === 'none' ? 'block' : 'none'; + } + else if (currentCategory === 'person') { + peopleFilters.style.display = peopleFilters.style.display === 'none' ? 'block' : 'none'; + } + else if (currentCategory === 'tv') { + tvFilters.style.display = tvFilters.style.display === 'none' ? 'block' : 'none'; + } + + if ((currentCategory === 'movie') && movieTvFilters.style.display !== 'none') { + toggleFiltersBtn.textContent = 'Close Filters'; + } + else if (currentCategory === 'person' && peopleFilters.style.display !== 'none') { + toggleFiltersBtn.textContent = 'Close Filters'; + } + else if (currentCategory === 'tv' && tvFilters.style.display !== 'none') { + toggleFiltersBtn.textContent = 'Close Filters'; + } + else { + toggleFiltersBtn.textContent = 'Filter Results'; + } + }); + + document.querySelectorAll('.category-buttons button').forEach(button => { + button.addEventListener('click', function() { + currentCategory = this.getAttribute('data-category'); + }); + }); +}); + function attachArrowKeyNavigation() { const categories = ['movie', 'tv', 'person']; let currentIndex = 0; @@ -272,42 +582,84 @@ function getMovieVerseData(input) { async function showResults(category) { showSpinner(); - localStorage.setItem('selectedCategory', category); - const searchQuery = localStorage.getItem('searchQuery'); - const movieName = `${getMovieCode()}`; - let movieUrl; + currentCategory = category; - if (category === 'movie') { - movieUrl = `https://${getMovieVerseData()}/3/search/movie?${generateMovieNames()}${movieName}&query=${encodeURIComponent(searchQuery)}`; - } - else if (category === 'tv') { - movieUrl = `https://${getMovieVerseData()}/3/search/tv?${generateMovieNames()}${movieName}&query=${encodeURIComponent(searchQuery)}`; - } - else { - movieUrl = `https://${getMovieVerseData()}/3/search/person?${generateMovieNames()}${movieName}&query=${encodeURIComponent(searchQuery)}`; - } + const searchQuery = localStorage.getItem('searchQuery') || ''; + document.getElementById('search-results-label').textContent = `Search Results for "${searchQuery}"`; - const searchLabel = document.getElementById('search-results-label'); - searchLabel.textContent = `Search results for "${searchQuery}"`; + const code = getMovieCode(); + const baseApiUrl = `https://${getMovieVerseData()}/3`; + let url = `${baseApiUrl}/search/${category}?${generateMovieNames()}${code}&query=${encodeURIComponent(searchQuery)}`; try { - const response = await fetch(movieUrl); - const data = await response.json(); - const sortedResults = data.results.sort((a, b) => b.popularity - a.popularity); - displayResults(sortedResults, category, searchQuery); - hideSpinner(); + const response = await fetch(url); + let data = await response.json(); + + if (category === 'movie') { + const genre = document.getElementById('genre-filter').value; + const year = category === 'movie' ? document.getElementById('year-filter').value : document.getElementById('year-filter').value; + const rating = parseFloat(document.getElementById('rating-filter').value); + const language = document.getElementById('language-filter').value; + + data.results = data.results.filter(item => { + const itemYear = category === 'movie' ? item.release_date?.substring(0, 4) : item.first_air_date?.substring(0, 4); + const itemRating = item.vote_average; + const itemGenres = item.genre_ids; + const itemLanguage = item.original_language; + + return (!genre || itemGenres.includes(parseInt(genre))) && + (!year || itemYear === year) && + (!rating || itemRating >= rating) && + (!language || itemLanguage === language); + }); + } + else if (category === 'person') { + const profession = document.getElementById('profession-filter').value; + const gender = document.getElementById('gender-filter').value; + + if (profession) { + data.results = data.results.filter(person => person.known_for_department && person.known_for_department.toLowerCase() === profession.toLowerCase()); + } + + if (gender) { + data.results = data.results.filter(person => person.gender.toString() === gender); + } + + const popularity = parseFloat(document.getElementById('popularity-filter').value); + if (!isNaN(popularity) && popularity > 0) { + data.results = data.results.filter(person => person.popularity >= popularity); + } + + data.results.sort((a, b) => b.popularity - a.popularity); + } + else if (category === 'tv') { + const genre = document.getElementById('genre-tv-filter').value; + const year = document.getElementById('year-tv-filter').value; + const rating = parseFloat(document.getElementById('rating-tv-filter').value); + const language = document.getElementById('language-tv-filter').value; + + data.results = data.results.filter(item => { + const itemYear = item.first_air_date?.substring(0, 4); + const itemRating = item.vote_average; + const itemGenres = item.genre_ids; + const itemLanguage = item.original_language; + + return (!genre || itemGenres.includes(parseInt(genre))) && + (!year || itemYear === year) && + (!rating || itemRating >= rating) && + (!language || itemLanguage === language); + }); + } + + displayResults(data.results, category, searchQuery); } catch (error) { - console.error('Error fetching search results:', error); - document.querySelector('.movie-match-container1').innerHTML = '

Error fetching results. Please try again later.

'; + console.log('Error fetching search results:', error); + } + finally { hideSpinner(); } - - updateBrowserURL(searchQuery); - document.title = `Search Results for "${searchQuery}" - The MovieVerse`; - - hideSpinner(); } document.querySelector('button[onclick="showResults(\'movie\')"]').addEventListener('click', function() { @@ -355,7 +707,7 @@ function displayResults(results, category, searchTerm) { const capitalizedCategory = category.charAt(0).toUpperCase() + category.slice(1); if (results.length === 0) { - container.innerHTML = `

No results found for "${searchTerm}" in the ${capitalizedCategory} category. Please try again with a different query or look for it in another category.

`; + container.innerHTML = `

No results found for "${searchTerm}" in the ${capitalizedCategory} category or no results with the specified filters found. Please try again with a different query or change your filters.

`; container.style.height = '800px'; return; } @@ -374,10 +726,21 @@ function showMovies(items, container, category) { const isMovie = item.title && hasVoteAverage; const isTvSeries = item.name && hasVoteAverage && category === 'tv'; - const title = item.title || item.name || "N/A"; - const overview = item.overview || 'No overview available.'; + let title = item.title || item.name || "N/A"; + const words = title.split(' '); + + if (words.length >= 9) { + words[8] = '...'; + title = words.slice(0, 9).join(' '); + } + + let overview = item.overview || 'No overview available.'; const biography = item.biography || 'Click to view the details of this person.'; + if (overview === '') { + overview = 'No overview available.'; + } + const { id, profile_path, poster_path } = item; const imagePath = profile_path || poster_path ? IMGPATH + (profile_path || poster_path) : null; @@ -405,10 +768,14 @@ function showMovies(items, container, category) { movieContentHTML += `
`; if (isPerson) { - movieContentHTML += `

Details:

${biography}
`; + const roleOverview = item.known_for_department === 'Directing' ? 'Director Overview' : 'Actor Overview'; + movieContentHTML += `

${roleOverview}:

${biography}
`; + } + else if (isTvSeries) { + movieContentHTML += `

TV Series Overview:

${overview}
`; } else { - movieContentHTML += `

Overview:

${overview}
`; + movieContentHTML += `

Movie Overview:

${overview}
`; } movieEl.innerHTML = movieContentHTML; @@ -420,16 +787,48 @@ function showMovies(items, container, category) { const response = await fetch(personDetailsUrl); const personDetails = await response.json(); if (personDetails.known_for_department === 'Directing') { + const directorVisits = JSON.parse(localStorage.getItem('directorVisits')) || {}; + const uniqueDirectorsViewed = JSON.parse(localStorage.getItem('uniqueDirectorsViewed')) || []; + + if (!uniqueDirectorsViewed.includes(id)) { + uniqueDirectorsViewed.push(id); + localStorage.setItem('uniqueDirectorsViewed', JSON.stringify(uniqueDirectorsViewed)); + } + + if (directorVisits[id]) { + directorVisits[id].count++; + } + else { + directorVisits[id] = { count: 1, name: personDetails.name || 'Unknown' }; + } + + localStorage.setItem('directorVisits', JSON.stringify(directorVisits)); localStorage.setItem('selectedDirectorId', id); window.location.href = 'director-details.html?' + id; } else { + const actorVisits = JSON.parse(localStorage.getItem('actorVisits')) || {}; + const uniqueActorsViewed = JSON.parse(localStorage.getItem('uniqueActorsViewed')) || []; + + if (!uniqueActorsViewed.includes(id)) { + uniqueActorsViewed.push(id); + localStorage.setItem('uniqueActorsViewed', JSON.stringify(uniqueActorsViewed)); + } + + if (actorVisits[id]) { + actorVisits[id].count++; + } + else { + actorVisits[id] = { count: 1, name: personDetails.name || 'Unknown' }; + } + + localStorage.setItem('actorVisits', JSON.stringify(actorVisits)); localStorage.setItem('selectedActorId', id); window.location.href = 'actor-details.html?' + id; } } catch (error) { - console.error('Error fetching person details:', error); + console.log('Error fetching person details:', error); } } else if (isMovie) { @@ -448,43 +847,41 @@ function showMovies(items, container, category) { }); } -function handleSignInOut() { - const isSignedIn = JSON.parse(localStorage.getItem('isSignedIn')) || false; +function handleDirectorClick(directorId, directorName) { + updateUniqueDirectorsViewed(directorId); + updateDirectorVisitCount(directorId, directorName); + localStorage.setItem('selectedDirectorId', directorId); + document.title = `${directorName} - Director's Details`; + window.location.href = 'director-details.html'; +} - if (isSignedIn) { - localStorage.setItem('isSignedIn', JSON.stringify(false)); - alert('You have been signed out.'); +function updateUniqueDirectorsViewed(directorId) { + let viewedDirectors = JSON.parse(localStorage.getItem('uniqueDirectorsViewed')) || []; + if (!viewedDirectors.includes(directorId)) { + viewedDirectors.push(directorId); + localStorage.setItem('uniqueDirectorsViewed', JSON.stringify(viewedDirectors)); } - else { - window.location.href = 'sign-in.html'; - return; +} + +function updateActorVisitCount(actorId, actorName) { + let actorVisits = JSON.parse(localStorage.getItem('actorVisits')) || {}; + if (!actorVisits[actorId]) { + actorVisits[actorId] = { count: 0, name: actorName }; } - updateSignInButtonState(); + actorVisits[actorId].count += 1; + localStorage.setItem('actorVisits', JSON.stringify(actorVisits)); } -function updateSignInButtonState() { - const isSignedIn = JSON.parse(localStorage.getItem('isSignedIn')) || false; - const signInText = document.getElementById('signInOutText'); - const signInIcon = document.getElementById('signInIcon'); - const signOutIcon = document.getElementById('signOutIcon'); - - if (isSignedIn) { - signInText.textContent = 'Sign Out'; - signInIcon.style.display = 'none'; - signOutIcon.style.display = 'inline-block'; - } - else { - signInText.textContent = 'Sign In'; - signInIcon.style.display = 'inline-block'; - signOutIcon.style.display = 'none'; +function updateDirectorVisitCount(directorId, directorName) { + let directorVisits = JSON.parse(localStorage.getItem('directorVisits')) || {}; + if (!directorVisits[directorId]) { + directorVisits[directorId] = { count: 0, name: directorName }; } -} -document.addEventListener("DOMContentLoaded", function() { - updateSignInButtonState(); - document.getElementById('googleSignInBtn').addEventListener('click', handleSignInOut); -}); + directorVisits[directorId].count += 1; + localStorage.setItem('directorVisits', JSON.stringify(directorVisits)); +} function getClassByRate(vote) { if (vote >= 8) { @@ -512,4 +909,4 @@ function updateBrowserURL(title) { function createNameSlug(title) { return title.toLowerCase().replace(/ /g, '-').replace(/[^\w-]/g, ''); -} +} \ No newline at end of file diff --git a/MovieVerse-Mobile/app/js/service-worker.js b/MovieVerse-Mobile/app/js/service-worker.js index 028f1cad..bcc2087d 100644 --- a/MovieVerse-Mobile/app/js/service-worker.js +++ b/MovieVerse-Mobile/app/js/service-worker.js @@ -1,83 +1,82 @@ const CACHE_NAME = 'movieverse-cache-v1'; const urlsToCache = [ - '../../index.html', - '../css/style.css', - '../css/trivia.css', - '../../index.js', - '../../manifest.json', - 'settings.js', - '../../images/favicon.ico', - '../../images/image.png', - 'chatbot.js', - 'movie-details.js', - 'movie-timeline.js', - 'quiz.js', - 'movie-details.js', - 'actor-details.js', - 'director-details.js', - '../html/about.html', - '../html/actor-details.html', - '../html/director-details.html', - '../html/movie-details.html', - '../html/movie-timeline.html', - '../html/trivia.html', - '../html/settings.html', - '../html/favorites.html', - '../html/chatbot.html', - '../html/settings.html', - '../html/privacy-policy.html', - '../html/terms-of-service.html', - '../../images/black.jpeg', - '../../images/blue.jpg', - '../../images/brown.jpg', - '../../images/green.jpg', - '../../images/gold.jpg', - '../../images/grey.png', - '../../images/orange.jpg', - '../../images/pink.png', - '../../images/purple.jpg', - '../../images/red.jpg', - '../../images/rose.png', - '../../images/silver.png', - '../../images/universe.jpg', - '../../images/universe.png', - '../../images/universe-1.png', - '../../images/universe-2.png', - '../../images/universe-2.jpg', - '../../images/universe-3.jpg', - '../../images/universe-4.png', - '../../images/universe-5.png', - '../../images/universe-6.png', - '../../images/universe-7.png', - '../../images/universe-8.png', - '../../images/universe-9.png', - '../../images/universe-10.png', - '../../images/yellow.jpg', - 'analytics.js', - '../html/analytics.html', - '../html/offline.html', + '/index.html', + '/MovieVerse-Frontend/css/style.css', + '/MovieVerse-Frontend/css/trivia.css', + '/index.js', + '/manifest.json', + '/MovieVerse-Frontend/js/settings.js', + '/images/favicon.ico', + '/images/image.png', + '/MovieVerse-Frontend/js/chatbot.js', + '/MovieVerse-Frontend/js/movie-details.js', + '/MovieVerse-Frontend/js/movie-timeline.js', + '/MovieVerse-Frontend/js/quiz.js', + '/MovieVerse-Frontend/js/actor-details.js', + '/MovieVerse-Frontend/js/director-details.js', + '/MovieVerse-Frontend/html/about.html', + '/MovieVerse-Frontend/html/actor-details.html', + '/MovieVerse-Frontend/html/director-details.html', + '/MovieVerse-Frontend/html/movie-details.html', + '/MovieVerse-Frontend/html/movie-timeline.html', + '/MovieVerse-Frontend/html/notifications.html', + '/MovieVerse-Frontend/html/trivia.html', + '/MovieVerse-Frontend/html/settings.html', + '/MovieVerse-Frontend/html/favorites.html', + '/MovieVerse-Frontend/html/chat.html', + '/MovieVerse-Frontend/html/chatbot.html', + '/MovieVerse-Frontend/html/privacy-policy.html', + '/MovieVerse-Frontend/html/terms-of-service.html', + '/images/black.webp', + '/images/blue.webp', + '/images/brown.webp', + '/images/green.webp', + '/images/gold.webp', + '/images/grey.webp', + '/images/orange.webp', + '/images/pink.webp', + '/images/purple.webp', + '/images/red.webp', + '/images/rose.webp', + '/images/silver.webp', + '/images/universe.webp', + '/images/universe-1.webp', + '/images/universe-1-small.webp', + '/images/universe-1-medium.webp', + '/images/universe-22.webp', + '/images/universe-2.webp', + '/images/universe-23.webp', + '/images/universe-3.webp', + '/images/universe-4.webp', + '/images/universe-5.webp', + '/images/universe-6.webp', + '/images/universe-7.webp', + '/images/universe-8.webp', + '/images/universe-9.webp', + '/images/universe-10.webp', + '/images/universe-11.webp', + '/images/universe-12.webp', + '/images/universe-13.webp', + '/images/universe-14.webp', + '/images/universe-15.webp', + '/images/universe-16.webp', + '/images/universe-17.webp', + '/images/universe-18.webp', + '/images/universe-19.webp', + '/images/universe-20.webp', + '/images/universe-21.webp', + '/images/yellow.webp', + '/MovieVerse-Frontend/js/analytics.js', + '/MovieVerse-Frontend/html/analytics.html', + '/MovieVerse-Frontend/html/offline.html', ]; self.addEventListener('install', event => { event.waitUntil( caches.open(CACHE_NAME) .then(cache => { - const cachePromises = urlsToCache.map(urlToCache => { - return fetch(urlToCache).then(response => { - if (response.ok) { - return cache.put(urlToCache, response); - } - console.warn(`Could not cache: ${urlToCache} - ${response.statusText}`); - return Promise.resolve(); - }).catch(error => { - console.warn(`Failed to fetch and cache: ${urlToCache}`, error); - return Promise.resolve(); - }); - }); - return Promise.all(cachePromises).then(() => { - console.log('Finished caching available resources'); - }); + return cache.addAll(urlsToCache); }) ); }); @@ -86,27 +85,30 @@ self.addEventListener('fetch', event => { event.respondWith( caches.match(event.request) .then(response => { - if (response) { - return response; - } - return fetch(event.request).catch(() => { - return caches.match('../html/offline.html'); - }); - }) + if (response) { + return response; + } + return fetch(event.request) + .then(fetchResponse => { + return caches.open(CACHE_NAME) + .then(cache => { + cache.put(event.request, fetchResponse.clone()); + return fetchResponse; + }); + }) + .catch(() => caches.match('/MovieVerse-Frontend/html/offline.html')); + }) ); }); self.addEventListener('activate', event => { - const cacheWhitelist = ['movieverse-cache-v1']; + const cacheWhitelist = [CACHE_NAME]; event.waitUntil( caches.keys().then(cacheNames => { return Promise.all( - cacheNames.map(cacheName => { - if (cacheWhitelist.indexOf(cacheName) === -1) { - return caches.delete(cacheName); - } - }) + cacheNames.filter(cacheName => !cacheWhitelist.includes(cacheName)) + .map(cacheName => caches.delete(cacheName)) ); }) ); -}); +}); \ No newline at end of file diff --git a/MovieVerse-Mobile/app/js/settings.js b/MovieVerse-Mobile/app/js/settings.js index 09cf3ef9..3a302e8c 100644 --- a/MovieVerse-Mobile/app/js/settings.js +++ b/MovieVerse-Mobile/app/js/settings.js @@ -1,51 +1,168 @@ +const DEFAULT_BACKGROUND_IMAGE = '../../images/universe-1.webp'; + document.addEventListener('DOMContentLoaded', () => { const bgSelect = document.getElementById('background-select'); const textColorInput = document.getElementById('text-color-input'); const fontSizeSelect = document.getElementById('font-size-select'); const resetButton = document.getElementById('reset-button'); + const deleteButton = document.getElementById('delete-uploaded-btn'); + const deleteImagesSection = document.getElementById('delete-images-section'); + const customImagesContainer = document.getElementById('custom-images-container'); + const deleteSelectedImagesBtn = document.getElementById('delete-selected-images-btn'); loadCustomBackgrounds(); loadSettings(); - bgSelect.addEventListener('change', function() { - document.body.style.backgroundImage = `url('${this.value}')`; - localStorage.setItem('backgroundImage', this.value); - }); + if (bgSelect) { + bgSelect.addEventListener('change', function() { + document.body.style.backgroundImage = `url('${this.value}')`; + localStorage.setItem('backgroundImage', this.value); + window.location.reload(); + }); + } - textColorInput.addEventListener('input', function() { - document.querySelectorAll('h1, h2, h3, p, a, span, div, button, input, select, textarea, label, li').forEach(element => { - element.style.color = this.value; + if (textColorInput) { + textColorInput.addEventListener('input', function () { + document.querySelectorAll('h1, h2, h3, p, a, span, div, button, input, select, textarea, label, li').forEach(element => { + element.style.color = this.value; + }); + localStorage.setItem('textColor', this.value); }); - localStorage.setItem('textColor', this.value); - }); + } - fontSizeSelect.addEventListener('change', function() { - const size = this.value === 'small' ? '12px' : this.value === 'medium' ? '16px' : '20px'; - document.body.style.fontSize = size; - localStorage.setItem('fontSize', this.value); - }); + if (fontSizeSelect) { + fontSizeSelect.addEventListener('change', function () { + const size = this.value === 'small' ? '12px' : this.value === 'medium' ? '16px' : '20px'; + document.body.style.fontSize = size; + localStorage.setItem('fontSize', this.value); + }); + } - resetButton.addEventListener('click', function() { - localStorage.removeItem('backgroundImage'); - localStorage.setItem('backgroundImage', '../../images/universe-1.png') - localStorage.removeItem('textColor'); - localStorage.removeItem('fontSize'); - window.location.reload(); - }); + if (resetButton) { + resetButton.addEventListener('click', function () { + localStorage.removeItem('backgroundImage'); + localStorage.setItem('backgroundImage', '../../images/universe-1.webp'); + localStorage.removeItem('textColor'); + localStorage.removeItem('fontSize'); + window.location.reload(); + }); + } + + if (deleteButton) { + deleteButton.addEventListener('click', function() { + if (deleteImagesSection.style.display === 'block') { + deleteImagesSection.style.display = 'none'; + } else { + deleteImagesSection.style.display = 'block'; + updateCustomImagesDisplay(); + } + }); + } + + if (deleteSelectedImagesBtn) { + deleteSelectedImagesBtn.addEventListener('click', () => { + const customImages = JSON.parse(localStorage.getItem('customImages')) || []; + const selectedIndexes = Array.from(document.querySelectorAll('.delete-checkbox:checked')).map(checkbox => parseInt(checkbox.value)); + + const updatedImages = customImages.filter((_, index) => !selectedIndexes.includes(index)); + localStorage.setItem('customImages', JSON.stringify(updatedImages)); + + updateCustomImagesDisplay(); + updateBackgroundSelectOptions(); + alert('Selected images have been deleted.'); + window.location.reload(); + }); + } + + function updateCustomImagesDisplay() { + const customImages = JSON.parse(localStorage.getItem('customImages')) || []; + customImagesContainer.innerHTML = ''; + + if (customImages.length === 0) { + customImagesContainer.innerHTML = '

No custom images uploaded.

'; + deleteSelectedImagesBtn.style.display = 'none'; + return; + } + + customImages.forEach((image, index) => { + const imageContainer = document.createElement('div'); + imageContainer.classList.add('image-container'); + + const checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + checkbox.classList.add('delete-checkbox'); + checkbox.value = index; + + const img = document.createElement('img'); + img.src = image.dataURL; + img.alt = image.name; + img.style.width = '100px'; + + const imageName = document.createElement('span'); + imageName.classList.add('image-name'); + imageName.textContent = image.name; + + // Toggle checkbox when image container is clicked + imageContainer.addEventListener('click', (e) => { + if (e.target !== checkbox) { // Prevent checkbox click event from toggling twice + checkbox.checked = !checkbox.checked; + } + }); + + imageContainer.appendChild(checkbox); + imageContainer.appendChild(img); + imageContainer.appendChild(imageName); + customImagesContainer.appendChild(imageContainer); + }); + + deleteSelectedImagesBtn.style.display = 'block'; + } function loadSettings() { let savedBg = localStorage.getItem('backgroundImage'); - const bgSelect = document.getElementById('background-select'); const customImages = JSON.parse(localStorage.getItem('customImages')) || []; const savedTextColor = localStorage.getItem('textColor'); const savedFontSize = localStorage.getItem('fontSize'); if (!savedBg) { - savedBg = '../../images/universe-1.png'; + savedBg = DEFAULT_BACKGROUND_IMAGE; + } + + const availableBackgrounds = [ + '../../images/universe-1.webp', '../../images/universe-2.webp', '../../images/universe-22.webp', + '../../images/universe-3.webp', '../../images/universe-4.webp', '../../images/universe-5.webp', + '../../images/universe-6.webp', '../../images/universe-7.webp', '../../images/universe-8.webp', + '../../images/universe-9.webp', '../../images/universe-10.webp', '../../images/universe-11.webp', + '../../images/universe-12.webp', '../../images/universe-13.webp', '../../images/universe-14.webp', + '../../images/universe-15.webp', '../../images/universe-16.webp', '../../images/universe-17.webp', + '../../images/universe-18.webp', '../../images/universe-19.webp', '../../images/universe-20.webp', + '../../images/universe-21.webp', '../../images/universe.webp', '../../images/universe-23.webp', + '../../images/black.webp', '../../images/grey.webp', '../../images/blue.webp', + '../../images/silver.webp', '../../images/gold.webp', '../../images/rose.webp', + '../../images/pink.webp', '../../images/red.webp', '../../images/green.webp', + '../../images/brown.webp', '../../images/purple.webp', '../../images/orange.webp', + '../../images/yellow.webp' + ]; + + if (!availableBackgrounds.includes(savedBg) && !customImages.find(image => image.dataURL === savedBg)) { + savedBg = DEFAULT_BACKGROUND_IMAGE; + localStorage.setItem('backgroundImage', savedBg); } - document.body.style.backgroundImage = `url('${savedBg}')`; + + if (savedBg) { + let imageUrl = savedBg; + if (savedBg === DEFAULT_BACKGROUND_IMAGE) { + if (window.innerWidth <= 680) { + imageUrl = '../../images/universe-1-small.webp'; + } + else if (window.innerWidth <= 1124) { + imageUrl = '../../images/universe-1-medium.webp'; + } + } + document.body.style.backgroundImage = `url('${imageUrl}')`; + } + const foundImage = customImages.find(image => image.dataURL === savedBg); - bgSelect.value = foundImage ? foundImage.dataURL : savedBg; if (savedTextColor) { document.querySelectorAll('h1, h2, h3, p, a, span, div, button, input, select, textarea, label, li').forEach(element => { @@ -53,61 +170,84 @@ document.addEventListener('DOMContentLoaded', () => { }); textColorInput.value = savedTextColor; } + if (savedFontSize) { const size = savedFontSize === 'small' ? '12px' : savedFontSize === 'medium' ? '16px' : '20px'; document.body.style.fontSize = size; fontSizeSelect.value = savedFontSize; } + + if (bgSelect) { + bgSelect.value = foundImage ? foundImage.dataURL : savedBg; + } } - document.getElementById('delete-uploaded-btn').addEventListener('click', function() { - deleteImagesPrompt(); - }); + function loadCustomBackgrounds() { + const bgSelect = document.getElementById('background-select'); + const customImages = JSON.parse(localStorage.getItem('customImages')) || []; + + if (bgSelect) { + customImages.forEach(image => { + const newOption = new Option(image.name, image.dataURL); + bgSelect.add(newOption); + }); + } + } }); -function loadCustomBackgrounds() { - const bgSelect = document.getElementById('background-select'); - const customImages = JSON.parse(localStorage.getItem('customImages')) || []; +document.addEventListener('DOMContentLoaded', () => { + const uploadButton = document.getElementById('upload-bg-btn'); - customImages.forEach(image => { - const newOption = new Option(image.name, image.dataURL); - bgSelect.add(newOption); - }); -} + if (!uploadButton) { + console.log('Upload button not found'); + return; + } -document.getElementById('upload-bg-btn').addEventListener('click', function() { - const fileInput = document.getElementById('custom-bg-upload'); - const imageNameInput = document.getElementById('custom-bg-name'); - const bgSelect = document.getElementById('background-select'); + uploadButton.addEventListener('click', function() { + const fileInput = document.getElementById('custom-bg-upload'); + const imageNameInput = document.getElementById('custom-bg-name'); + const bgSelect = document.getElementById('background-select'); - if (fileInput.files.length > 0) { - const file = fileInput.files[0]; - const customImages = JSON.parse(localStorage.getItem('customImages')) || []; - const totalSize = customImages.reduce((sum, img) => sum + img.dataURL.length, 0); - const quota = 4.5 * 1024 * 1024; + if (fileInput && fileInput.files.length > 0) { + const file = fileInput.files[0]; + const customImages = JSON.parse(localStorage.getItem('customImages')) || []; + const totalSize = customImages.reduce((sum, img) => sum + img.dataURL.length, 0); + const quota = 4.5 * 1024 * 1024; // 4.5 MB - if (totalSize >= quota) { - handleQuotaExceedance(); - } - else { - if (file.size > 204800) { // 200KB - resizeImage(file, 204800, (resizedDataUrl) => { + if (totalSize >= quota) { + handleQuotaExceedance(); + window.location.reload(); + return; + } + + if (file.size > 204800) { // 200 KB + resizeImage(file, 204800, (resizedDataUrl, err) => { + if (err) { + alert(`Error resizing the image due to a limitation in your browser. Browser error: ${err.message} Your image might still appear as the background, but it will not be stable. We recommend deleting it and then using a different browser or uploading an image smaller than 1MB.`); + return; + } processImageUpload(resizedDataUrl, imageNameInput, bgSelect); alert('The uploaded image was resized to fit the size limit of 200KB.'); + window.location.reload(); }); } else { const reader = new FileReader(); reader.onload = function (e) { processImageUpload(e.target.result, imageNameInput, bgSelect); + window.location.reload(); + }; + reader.onerror = function () { + alert('Error reading the file.'); + window.location.reload(); }; reader.readAsDataURL(file); } } - } - else { - alert('Please select an image to upload.'); - } + else { + alert('Please select an image to upload.'); + } + }); }); function handleQuotaExceedance() { @@ -178,37 +318,56 @@ function processImageUpload(dataUrl, imageNameInput, bgSelect) { } function resizeImage(file, maxSize, callback) { + if (!(window.FileReader && window.Blob && window.HTMLCanvasElement)) { + callback(null, new Error('Your browser does not support resizing images. Please use a different browser or upload an image smaller than 200KB.')); + return; + } + const reader = new FileReader(); reader.onload = function(e) { const img = new Image(); img.onload = function() { - let canvas = document.createElement('canvas'); - let ctx = canvas.getContext('2d'); - - let width = img.width; - let height = img.height; - - if (width > height) { - if (width > maxSize) { - height *= maxSize / width; - width = maxSize; + try { + let canvas = document.createElement('canvas'); + let ctx = canvas.getContext('2d'); + + let width = img.width; + let height = img.height; + + if (width > height) { + if (width > maxSize) { + height *= maxSize / width; + width = maxSize; + } } - } - else { - if (height > maxSize) { - width *= maxSize / height; - height = maxSize; + else { + if (height > maxSize) { + width *= maxSize / height; + height = maxSize; + } } - } - canvas.width = width; - canvas.height = height; - ctx.drawImage(img, 0, 0, width, height); + canvas.width = width; + canvas.height = height; + ctx.drawImage(img, 0, 0, width, height); - callback(canvas.toDataURL('image/jpeg')); + callback(canvas.toDataURL('image/jpeg'), null); + + canvas.height = 0; + canvas.width = 0; + canvas = null; + } + catch (error) { + callback(null, error); + } + }; + img.onerror = function() { + callback(null, new Error('Failed to load the image.')); }; img.src = e.target.result; }; + reader.onerror = function() { + callback(null, new Error('Failed to read the image file.')); + }; reader.readAsDataURL(file); } - diff --git a/MovieVerse-Mobile/app/js/sign-in.js b/MovieVerse-Mobile/app/js/sign-in.js index 0e3d5140..5cd52e60 100644 --- a/MovieVerse-Mobile/app/js/sign-in.js +++ b/MovieVerse-Mobile/app/js/sign-in.js @@ -45,21 +45,34 @@ const db = getFirestore(app); document.getElementById('signInForm').addEventListener('submit', async function(event) { event.preventDefault(); - const email = document.getElementById('signInEmail').value; - const password = document.getElementById('signInPassword').value; + try { - const usersRef = collection(db, "MovieVerseUsers"); - const q = query(usersRef, where("email", "==", email), where("password", "==", password)); - const querySnapshot = await getDocs(q); + const email = document.getElementById('signInEmail').value; + const password = document.getElementById('signInPassword').value; - if (!querySnapshot.empty) { - alert('Successfully signed in!'); - localStorage.setItem('isSignedIn', JSON.stringify(true)); - localStorage.setItem('currentlySignedInMovieVerseUser', email); - window.location.href = '../../index.html'; + const usersRef = collection(db, "MovieVerseUsers"); + const q = query(usersRef, where("email", "==", email), where("password", "==", password)); + const querySnapshot = await getDocs(q); + + if (!querySnapshot.empty) { + alert('Successfully signed in!'); + localStorage.setItem('isSignedIn', JSON.stringify(true)); + localStorage.setItem('currentlySignedInMovieVerseUser', email); + window.location.href = '../../index.html'; + } else { + alert('Invalid email or password. Ensure that you have entered a correct combination of email and password - one that we have on file.'); + } } - else { - alert('Invalid email or password. Ensure that you have entered a correct combination of email and password - one that we have on file.'); + catch (error) { + console.error("Error fetching user list: ", error); + if (error.code === 'resource-exhausted') { + const noUserSelected = document.getElementById('signInForm'); + if (noUserSelected) { + noUserSelected.innerHTML = "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!"; + noUserSelected.style.height = '350px'; + } + hideSpinner(); + } } }); diff --git a/MovieVerse-Mobile/app/js/triviaModule.js b/MovieVerse-Mobile/app/js/triviaModule.js new file mode 100644 index 00000000..4ae5c28c --- /dev/null +++ b/MovieVerse-Mobile/app/js/triviaModule.js @@ -0,0 +1,69 @@ +import { initializeApp } from "https://www.gstatic.com/firebasejs/10.8.0/firebase-app.js"; +import { getFirestore, doc, setDoc, getDoc } from "https://www.gstatic.com/firebasejs/10.8.0/firebase-firestore.js"; + +const firebaseConfig = { + apiKey: atob("QUl6YVN5REw2a1FuU2ZVZDhVdDhIRnJwS3VpdnF6MXhkWG03aw=="), + authDomain: atob("bW92aWV2ZXJzZS1hcHAuZmlyZWJhc2VhcHAuY29t"), + projectId: "movieverse-app", + storageBucket: atob("bW92aWV2ZXJzZS1hcHAuYXBwc3BvdC5jb20="), + messagingSenderId: atob("ODAyOTQzNzE4ODcx"), + appId: atob("MTo4MDI5NDM3MTg4NzE6d2ViOjQ4YmM5MTZjYzk5ZTI3MjQyMTI3OTI=") +}; + +const app = initializeApp(firebaseConfig); + +const db = getFirestore(app); + +export async function updateTriviaStats(currentUserEmail, correctAnswers, totalQuestions) { + if (!currentUserEmail) { + let triviaStats = JSON.parse(localStorage.getItem('triviaStats')) || { totalCorrect: 0, totalAttempted: 0 }; + triviaStats.totalCorrect += correctAnswers; + triviaStats.totalAttempted += totalQuestions; + localStorage.setItem('triviaStats', JSON.stringify(triviaStats)); + } + else { + try { + const statsRef = doc(db, 'userTriviaStats', currentUserEmail); + const docSnap = await getDoc(statsRef); + let triviaStats = docSnap.exists() ? docSnap.data() : {totalCorrect: 0, totalAttempted: 0}; + triviaStats.totalCorrect += correctAnswers; + triviaStats.totalAttempted += totalQuestions; + await setDoc(statsRef, triviaStats, {merge: true}); + localStorage.setItem('triviaStats', JSON.stringify(triviaStats)); + } + catch (error) { + if (error.code === 'resource-exhausted') { + let triviaStats = JSON.parse(localStorage.getItem('triviaStats')) || { totalCorrect: 0, totalAttempted: 0 }; + triviaStats.totalCorrect += correctAnswers; + triviaStats.totalAttempted += totalQuestions; + localStorage.setItem('triviaStats', JSON.stringify(triviaStats)); + } + } + } +} + +export async function getTriviaStats(currentUserEmail) { + if (!currentUserEmail) { + return JSON.parse(localStorage.getItem('triviaStats')) || { totalCorrect: 0, totalAttempted: 0 }; + } + else { + const statsRef = doc(db, 'userTriviaStats', currentUserEmail); + try { + const docSnap = await getDoc(statsRef); + if (docSnap.exists()) { + console.log("Fetched trivia stats from Firebase:", docSnap.data()); + return docSnap.data(); + } + else { + console.log("No trivia stats found in Firebase for:", currentUserEmail); + return { totalCorrect: 0, totalAttempted: 0 }; + } + } + catch (error) { + if (error.code === 'resource-exhausted') { + console.error("Firebase quota exceeded, fetching trivia stats from localStorage."); + return JSON.parse(localStorage.getItem('triviaStats')) || {totalCorrect: 0, totalAttempted: 0}; + } + } + } +} diff --git a/MovieVerse-Mobile/app/js/tv-details.js b/MovieVerse-Mobile/app/js/tv-details.js index b92ee4fe..ebadb344 100644 --- a/MovieVerse-Mobile/app/js/tv-details.js +++ b/MovieVerse-Mobile/app/js/tv-details.js @@ -20,7 +20,7 @@ const form = document.getElementById("form1"); const SEARCHPATH = `https://${getMovieVerseData()}/3/search/movie?&${generateMovieNames()}${getMovieCode()}&query=`; const main = document.getElementById("main"); -const IMGPATH = "https://image.tmdb.org/t/p/w1280"; +const IMGPATH = "https://image.tmdb.org/t/p/w780"; const searchTitle = document.getElementById("search-title"); let initialMainContent; @@ -55,7 +55,7 @@ async function fetchGenreMap() { localStorage.setItem('genreMap', JSON.stringify(genreMap)); } catch (error) { - console.error('Error fetching genre map:', error); + console.log('Error fetching genre map:', error); } } @@ -87,7 +87,7 @@ async function rotateUserStats() { { label: "Favorite Movies", getValue: () => { - const favoritedMovies = JSON.parse(localStorage.getItem('favoritesMovies')) || []; + const favoritedMovies = JSON.parse(localStorage.getItem('moviesFavorited')) || []; return favoritedMovies.length; } }, @@ -95,8 +95,37 @@ async function rotateUserStats() { label: "Favorite Genre", getValue: () => { const mostCommonGenreCode = getMostCommonGenre(); - const genreMap = JSON.parse(localStorage.getItem('genreMap')) || {}; - return genreMap[mostCommonGenreCode] || 'Not Available'; + const genreMapString = localStorage.getItem('genreMap'); + if (!genreMapString) { + 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'; + } + + let genreObject; + if (Array.isArray(genreMap)) { + genreObject = genreMap.reduce((acc, genre) => { + acc[genre.id] = genre.name; + return acc; + }, {}); + } + 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'; + } + + return genreObject[mostCommonGenreCode] || 'Not Available'; } }, { label: "Watchlists Created", getValue: () => localStorage.getItem('watchlistsCreated') || 0 }, @@ -229,31 +258,16 @@ document.addEventListener('DOMContentLoaded', rotateUserStats); function setStarRating(rating) { const stars = document.querySelectorAll('.rating .star'); stars.forEach(star => { - star.style.color = star.dataset.value > rating ? 'grey' : 'gold'; + star.style.color = star.dataset.value > rating ? 'white' : 'gold'; }); document.getElementById('rating-value').textContent = `${rating}.0/5.0`; } -document.addEventListener('DOMContentLoaded', () => { - setInitialStarRating(tvSeriesId); -}); - function getMovieVerseData(input) { return String.fromCharCode(97, 112, 105, 46, 116, 104, 101, 109, 111, 118, 105, 101, 100, 98, 46, 111, 114, 103); } -function setInitialStarRating(tvSeriesId) { - const savedRatings = JSON.parse(localStorage.getItem('tvSeriesRatings')) || {}; - const tvSeriesRating = savedRatings[tvSeriesId]; - if (tvSeriesRating) { - setStarRating(tvSeriesRating); - } - else { - setStarRating(0); - } -} - document.querySelectorAll('.rating .star').forEach(star => { star.addEventListener('mouseover', (e) => { setStarRating(e.target.dataset.value); @@ -532,7 +546,6 @@ async function fetchTvDetails(tvSeriesId) { const response = await fetch(urlWithAppend); const tvSeriesDetails = await response.json(); const imdbId = tvSeriesDetails.external_ids.imdb_id; - const imdbRating = await fetchTVRatings(imdbId); populateTvSeriesDetails(tvSeriesDetails, imdbRating); @@ -551,25 +564,23 @@ async function fetchTvDetails(tvSeriesId) {

TV series details not found - Try again with another TV series

`; - console.error('Error fetching TV series details:', error); + console.log('Error fetching TV series details:', error); hideSpinner(); } } async function fetchTVRatings(imdbId) { - const omdbCode = `${getMovieCode2()}`; - const omdb = `https://${getMovieActor()}/?i=${imdbId}&${getMovieName()}${omdbCode}`; + const fff = `60a09d79`; + const link = `https://${getMovieActor()}/?i=${imdbId}&${getMovieName()}${fff}`; try { - const response = await fetch(omdb); + const response = await fetch(link); const data = await response.json(); - let imdbRating = data.imdbRating ? data.imdbRating : 'N/A'; - - return imdbRating; + return imdbRating = data.imdbRating ? data.imdbRating : 'IMDb data unavailable but you can check it out by clicking here'; } catch (error) { - console.error('Error fetching TV series ratings:', error); + console.log('Error fetching TV series ratings:', error); return 'N/A'; } } @@ -584,17 +595,21 @@ function getCountryName(code) { return regionNames.of(code); } -function populateTvSeriesDetails(tvSeries, imdbRating) { +async function populateTvSeriesDetails(tvSeries, imdbRating) { const title = tvSeries.name || 'Title not available'; document.getElementById('movie-title').textContent = title; - document.title = tvSeries.name + " - TV Series"; + document.title = tvSeries.name + " - TV Series Details"; - const posterPath = tvSeries.poster_path ? `https://image.tmdb.org/t/p/w1280${tvSeries.poster_path}` : 'path/to/default/poster.jpg'; + const posterPath = tvSeries.poster_path ? `https://image.tmdb.org/t/p/w780${tvSeries.poster_path}` : 'path/to/default/poster.jpg'; document.getElementById('movie-image').src = posterPath; document.getElementById('movie-image').alt = `Poster of ${title}`; let detailsHTML = `

Overview: ${tvSeries.overview || 'Overview not available.'}

`; + detailsHTML += `

Original Title: ${tvSeries.original_name || 'Not available'}

`; + + detailsHTML += `

Tagline: ${tvSeries.tagline || 'Not available'}

`; + const genres = tvSeries.genres && tvSeries.genres.length ? tvSeries.genres.map(genre => genre.name).join(', ') : 'Genres not available'; detailsHTML += `

Genres: ${genres}

`; @@ -609,7 +624,7 @@ function populateTvSeriesDetails(tvSeries, imdbRating) { const voteAverage = tvSeries.vote_average ? tvSeries.vote_average.toFixed(1) : 'N/A'; const voteCount = tvSeries.vote_count ? tvSeries.vote_count.toLocaleString() : 'N/A'; - detailsHTML += `

User Rating: ${(voteAverage / 2).toFixed(1)}/5.0 (based on ${voteCount} votes)

`; + detailsHTML += `

MovieVerse User Rating: ${(voteAverage / 2).toFixed(1)}/5.0 (based on ${voteCount} votes)

`; if (tvSeries.external_ids && tvSeries.external_ids.imdb_id) { const imdbId = tvSeries.external_ids.imdb_id; @@ -617,15 +632,34 @@ function populateTvSeriesDetails(tvSeries, imdbRating) { detailsHTML += `

IMDb Rating: ${imdbRating}

`; } else { - detailsHTML += `

IMDb Rating: N/A

`; + detailsHTML += `

IMDb Rating: IMDb rating not available

`; } - const homepage = tvSeries.homepage ? `Visit` : 'Not available'; + const tmdbRating = tvSeries.vote_average ? tvSeries.vote_average.toFixed(1) : 'N/A'; + detailsHTML += `

TMDB Rating: ${tmdbRating}/10.0

`; + + const homepage = tvSeries.homepage ? `Visit homepage` : 'Not available'; detailsHTML += `

Homepage: ${homepage}

`; + detailsHTML += `

Seasons: ${tvSeries.number_of_seasons || 0}, Episodes: ${tvSeries.number_of_episodes || 0}

`; + + if (tvSeries.origin_country && tvSeries.origin_country.length > 0) { + const countryNames = tvSeries.origin_country.map(code => getCountryName(code)).join(', '); + detailsHTML += `

Country of Origin: ${countryNames}

`; + } + else { + detailsHTML += `

Country of Origin: Information not available

`; + } + + const languageName = getLanguageName(tvSeries.original_language); + detailsHTML += `

Original Language: ${languageName}

`; + + const productionCountries = tvSeries.production_countries && tvSeries.production_countries.length > 0 ? tvSeries.production_countries.map(country => getCountryName(country.iso_3166_1)).join(', ') : 'Information not available'; + detailsHTML += `

Production Countries: ${productionCountries}

`; + if (tvSeries.created_by && tvSeries.created_by.length) { const creatorsLinks = tvSeries.created_by.map(creator => - `${creator.name}` + `${creator.name}` ).join(', '); detailsHTML += `

Directors: ${creatorsLinks}

`; } @@ -635,7 +669,8 @@ function populateTvSeriesDetails(tvSeries, imdbRating) { if (tvSeries.credits && tvSeries.credits.cast && tvSeries.credits.cast.length) { let castHTML = tvSeries.credits.cast.slice(0, 100).map(castMember => { - return `${castMember.name}`; + const escapedName = castMember.name.replace(/'/g, "\\'"); + return `${castMember.name}`; }).join(', '); detailsHTML += `

Cast: ${castHTML}

`; } @@ -663,47 +698,276 @@ function populateTvSeriesDetails(tvSeries, imdbRating) { detailsHTML += `

Similar TV Series: Information not available

`; } - detailsHTML += `

Tagline: ${tvSeries.tagline || 'Not available'}

`; + if (tvSeries.last_episode_to_air) { + detailsHTML += `

Last Episode: ${tvSeries.last_episode_to_air.name || 'Title not available'} - "${tvSeries.last_episode_to_air.overview || 'Overview not available.'}"

`; + } - detailsHTML += `

Seasons: ${tvSeries.number_of_seasons || 0}, Episodes: ${tvSeries.number_of_episodes || 0}

`; + const tvSeriesTitleEncoded = encodeURIComponent(title); + const streamingProviders = await fetchTvSeriesStreamingLinks(tvSeries.id); + const streamingHTML = streamingProviders.length > 0 ? streamingProviders.map(provider => { + let providerLink = `https://www.google.com/search?q=watch+${tvSeriesTitleEncoded}+on+${encodeURIComponent(provider.provider_name)}`; + switch(provider.provider_name.toLowerCase()) { + case 'netflix': + providerLink = `https://www.netflix.com/search?q=${tvSeriesTitleEncoded}`; + break; + case 'disney plus': + providerLink = `https://www.disneyplus.com/search?q=${tvSeriesTitleEncoded}`; + break; + case 'hbo max': + providerLink = `https://www.hbomax.com/search?q=${tvSeriesTitleEncoded}`; + break; + case 'hulu': + providerLink = `https://www.hulu.com/search?q=${tvSeriesTitleEncoded}`; + break; + case 'amazon prime video': + providerLink = `https://www.amazon.com/s?k=${tvSeriesTitleEncoded}`; + break; + case 'apple tv plus': + providerLink = `https://tv.apple.com/search?term=${tvSeriesTitleEncoded}`; + break; + case 'stan': + providerLink = `https://www.stan.com.au/search?q=${tvSeriesTitleEncoded}`; + break; + case 'player': + providerLink = `https://player.pl/szukaj?search=${tvSeriesTitleEncoded}`; + break; + } - // if (tvSeries.seasons && tvSeries.seasons.length) { - // const seasonsToShow = tvSeries.seasons.slice(0, 3); - // - // seasonsToShow.forEach(season => { - // detailsHTML += `

${season.name || 'Season information not available'}: ${season.overview || 'Overview not available.'}

`; - // }); - // } + return ` + ${provider.provider_name} + `; + }).join('') + ` + JustWatch + ` : 'No streaming options available.'; - if (tvSeries.origin_country && tvSeries.origin_country.length > 0) { - const countryNames = tvSeries.origin_country.map(code => getCountryName(code)).join(', '); - detailsHTML += `

Country of Origin: ${countryNames}

`; + detailsHTML += `

Streaming Options: ${streamingHTML}

`; + + if (tvSeries.keywords && tvSeries.keywords.results && tvSeries.keywords.results.length) { + let keywordsHTML = tvSeries.keywords.results.map(keyword => keyword.name).join(', '); + detailsHTML += `

Keywords: ${keywordsHTML}

`; } else { - detailsHTML += `

Country of Origin: Information not available

`; + detailsHTML += `

Keywords: Information not available

`; } - const languageName = getLanguageName(tvSeries.original_language); - detailsHTML += `

Original Language: ${languageName}

`; + const mediaUrl = `https://${getMovieVerseData()}/3/tv/${tvSeries.id}/images?${generateMovieNames()}${getMovieCode()}`; + const mediaResponse = await fetch(mediaUrl); + const mediaData = await mediaResponse.json(); + const images = mediaData.backdrops; + + const detailsContainer = document.getElementById('movie-description'); + + let mediaContainer = document.getElementById('media-container'); + if (!mediaContainer) { + mediaContainer = document.createElement('div'); + mediaContainer.id = 'media-container'; + mediaContainer.style = ` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + position: relative; + width: 450px; + margin: 20px auto; + overflow: hidden; + max-width: 100%; + box-sizing: border-box; + `; + detailsContainer.appendChild(mediaContainer); + } - if (tvSeries.last_episode_to_air) { - detailsHTML += `

Last Episode: ${tvSeries.last_episode_to_air.name || 'Title not available'} - "${tvSeries.last_episode_to_air.overview || 'Overview not available.'}"

`; + let mediaTitle = document.getElementById('media-title'); + if (!mediaTitle) { + mediaTitle = document.createElement('p'); + mediaTitle.id = 'media-title'; + mediaTitle.textContent = 'Media:'; + mediaTitle.style = ` + font-weight: bold; + align-self: start; + margin-bottom: 5px; + `; } - if (tvSeries.keywords && tvSeries.keywords.results && tvSeries.keywords.results.length) { - let keywordsHTML = tvSeries.keywords.results.map(keyword => keyword.name).join(', '); - detailsHTML += `

Keywords: ${keywordsHTML}

`; + let imageElement = document.getElementById('series-media-image'); + if (!imageElement) { + imageElement = document.createElement('img'); + imageElement.id = 'series-media-image'; + imageElement.style = ` + max-width: 100%; + max-height: 210px; + transition: opacity 0.5s ease-in-out; + opacity: 1; + border-radius: 16px; + cursor: pointer; + `; + imageElement.loading = 'lazy'; + mediaContainer.appendChild(imageElement); } - else { - detailsHTML += `

Keywords: Information not available

`; + + if (images.length > 0) { + imageElement.src = `https://image.tmdb.org/t/p/w780${images[0].file_path}`; + } + + imageElement.addEventListener('click', function() { + let imageUrl = this.src.replace('w780', 'w1280'); + const modalHtml = ` +
+ Media Image + × +
+ `; + document.body.insertAdjacentHTML('beforeend', modalHtml); + const modal = document.getElementById('image-modal'); + const closeModalBtn = document.getElementById('removeBtn'); + + closeModalBtn.onclick = function() { + modal.remove(); + }; + + modal.addEventListener('click', function(event) { + if (event.target === this) { + this.remove(); + } + }); + }); + + let prevButton = document.getElementById('prev-media-button'); + let nextButton = document.getElementById('next-media-button'); + if (!prevButton || !nextButton) { + prevButton = document.createElement('button'); + nextButton = document.createElement('button'); + prevButton.id = 'prev-media-button'; + nextButton.id = 'next-media-button'; + prevButton.innerHTML = ''; + nextButton.innerHTML = ''; + + [prevButton, nextButton].forEach(button => { + button.style = ` + position: absolute; + top: 50%; + transform: translateY(-50%); + background-color: #7378c5; + color: white; + border-radius: 8px; + height: 30px; + width: 30px; + border: none; + cursor: pointer; + `; + button.onmouseover = () => button.style.backgroundColor = '#ff8623'; + button.onmouseout = () => button.style.backgroundColor = '#7378c5'; + }); + + prevButton.style.left = '0'; + nextButton.style.right = '0'; + + mediaContainer.appendChild(prevButton); + mediaContainer.appendChild(nextButton); + } + + let currentIndex = 0; + prevButton.onclick = () => navigateMedia(images, imageElement, -1); + nextButton.onclick = () => navigateMedia(images, imageElement, 1); + + function navigateMedia(images, imgElement, direction) { + currentIndex += direction; + if (currentIndex < 0) { + currentIndex = images.length - 1; + } else if (currentIndex >= images.length) { + currentIndex = 0; + } + imgElement.style.opacity = '0'; + setTimeout(() => { + imgElement.src = `https://image.tmdb.org/t/p/w780${images[currentIndex].file_path}`; + imgElement.style.opacity = '1'; + }, 420); + } + + if (window.innerWidth <= 767) { + mediaContainer.style.width = 'calc(100% - 40px)'; + } + + if (images.length === 0) { + mediaContainer.innerHTML = '

No media available

'; } document.getElementById('movie-description').innerHTML = detailsHTML; + document.getElementById('movie-description').appendChild(mediaTitle); + document.getElementById('movie-description').appendChild(mediaContainer); +} + +async function fetchTvSeriesStreamingLinks(tvSeriesId) { + const url = `https://${getMovieVerseData()}/3/tv/${tvSeriesId}/watch/providers?${generateMovieNames()}${getMovieCode()}`; + try { + const response = await fetch(url); + const data = await response.json(); + const results = data.results || {}; + let providersMap = {}; + + Object.values(results).forEach(region => { + if (region.flatrate) { + region.flatrate.forEach(provider => { + providersMap[provider.provider_id] = provider; + }); + } + }); + + return Object.values(providersMap).slice(0, 7); + } + catch (error) { + console.error('Error fetching TV series streaming links:', error); + return []; + } +} + +function updateUniqueDirectorsViewed(directorId) { + let viewedDirectors = JSON.parse(localStorage.getItem('uniqueDirectorsViewed')) || []; + if (!viewedDirectors.includes(directorId)) { + viewedDirectors.push(directorId); + localStorage.setItem('uniqueDirectorsViewed', JSON.stringify(viewedDirectors)); + } +} + +function updateActorVisitCount(actorId, actorName) { + let actorVisits = JSON.parse(localStorage.getItem('actorVisits')) || {}; + if (!actorVisits[actorId]) { + actorVisits[actorId] = { count: 0, name: actorName }; + } + + actorVisits[actorId].count += 1; + localStorage.setItem('actorVisits', JSON.stringify(actorVisits)); +} + +function updateDirectorVisitCount(directorId, directorName) { + let directorVisits = JSON.parse(localStorage.getItem('directorVisits')) || {}; + if (!directorVisits[directorId]) { + directorVisits[directorId] = { count: 0, name: directorName }; + } + + directorVisits[directorId].count += 1; + localStorage.setItem('directorVisits', JSON.stringify(directorVisits)); } -function selectActorId(actorId) { +function selectActorId(actorId, actorName) { + const actorVisits = JSON.parse(localStorage.getItem('actorVisits')) || {}; + const uniqueActorsViewed = JSON.parse(localStorage.getItem('uniqueActorsViewed')) || []; + + if (!uniqueActorsViewed.includes(actorId)) { + uniqueActorsViewed.push(actorId); + localStorage.setItem('uniqueActorsViewed', JSON.stringify(uniqueActorsViewed)); + } + + if (actorVisits[actorId]) { + actorVisits[actorId].count++; + } + else { + actorVisits[actorId] = { count: 1, name: actorName }; + } + + localStorage.setItem('actorVisits', JSON.stringify(actorVisits)); + localStorage.setItem('selectedActorId', actorId); - window.location.href = 'actor-details.html' + window.location.href = 'actor-details.html'; } function selectTvSeriesId(tvSeriesId) { @@ -724,8 +988,11 @@ function hideSpinner() { document.getElementById('myModal').classList.remove('modal-visible'); } -function handleCreatorClick(creatorId) { +function handleCreatorClick(creatorId, creatorName) { localStorage.setItem('selectedDirectorId', creatorId); + document.title = `${creatorName} - Director's Details`; + updateUniqueDirectorsViewed(creatorId); + updateDirectorVisitCount(creatorId, creatorName); window.location.href = 'director-details.html'; } @@ -740,7 +1007,7 @@ document.addEventListener('DOMContentLoaded', () => { document.getElementById('clear-search-btn').style.display = 'none'; - const savedRatings = JSON.parse(localStorage.getItem('movieRatings')) || {}; + const savedRatings = JSON.parse(localStorage.getItem('tvSeriesRatings')) || {}; const movieRating = savedRatings[tvSeriesId] || 0; setStarRating(movieRating); }); @@ -782,7 +1049,7 @@ async function showMovieOfTheDay() { } } catch (error) { - console.error('Error fetching movie:', error); + console.log('Error fetching movie:', error); fallbackMovieSelection(); } } diff --git a/MovieVerse-Mobile/app/js/user-profile.js b/MovieVerse-Mobile/app/js/user-profile.js index 0db0e1ac..f5f730f1 100644 --- a/MovieVerse-Mobile/app/js/user-profile.js +++ b/MovieVerse-Mobile/app/js/user-profile.js @@ -1,6 +1,7 @@ import { initializeApp } from "https://www.gstatic.com/firebasejs/10.8.0/firebase-app.js"; -import { getFirestore, doc, getDoc, setDoc, deleteField } from "https://www.gstatic.com/firebasejs/10.8.0/firebase-firestore.js"; -import { getStorage, ref, uploadBytes, getDownloadURL, deleteObject } from "https://www.gstatic.com/firebasejs/10.8.0/firebase-storage.js"; +import { getFirestore, doc, getDoc, setDoc, query, collection, where, getDocs, serverTimestamp, deleteDoc } from "https://www.gstatic.com/firebasejs/10.8.0/firebase-firestore.js"; +import { getAverageMovieRating } from './ratings-module.js'; +import { getTriviaStats } from './triviaModule.js'; function showSpinner() { document.getElementById('myModal').classList.add('modal-visible'); @@ -54,82 +55,445 @@ const db = getFirestore(app); document.addEventListener('DOMContentLoaded', function() { handleProfileDisplay(); setupEventListeners(); + setupSearchListeners(); }); +function updateProgressCircles(movieRating, triviaScore) { + const movieRatingPercent = movieRating; + const triviaScorePercent = triviaScore; + + setProgress(document.getElementById('avgMovieRatingCircle'), document.getElementById('avgMovieRatingText'), movieRatingPercent); + setProgress(document.getElementById('avgTriviaScoreCircle'), document.getElementById('avgTriviaScoreText'), triviaScorePercent); +} + +function setProgress(circle, text, percent) { + const radius = circle.r.baseVal.value; + const circumference = radius * 2 * Math.PI; + + circle.style.transition = 'none'; + circle.style.strokeDasharray = `${circumference} ${circumference}`; + circle.style.strokeDashoffset = circumference; + circle.getBoundingClientRect(); + + setTimeout(() => { + const offset = circumference - (percent / 100) * circumference; + circle.style.transition = 'stroke-dashoffset 0.6s ease-out, stroke 0.6s ease'; + circle.style.strokeDashoffset = offset; + circle.style.setProperty('--progress-color', percent > 50 ? '#4CAF50' : '#2196F3'); + text.textContent = `${Math.round(percent)}%`; + text.style.opacity = 1; + }, 10); +} + function handleProfileDisplay() { - showSpinner(); const isSignedIn = JSON.parse(localStorage.getItem('isSignedIn')) || false; const userEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); - const profileKey = `profileInfo-${userEmail}`; - const profile = JSON.parse(localStorage.getItem(profileKey)) || {}; const welcomeMessage = document.getElementById('welcomeMessage'); - const profileContainer = document.getElementById('profileContainer'); const signInPrompt = document.getElementById('signInPrompt'); + const viewMyProfileBtn = document.getElementById('viewMyProfileBtn'); + const profileContainer = document.getElementById('profileContainer'); + profileContainer.style.display = 'none'; + + showSpinner(); if (isSignedIn && userEmail) { - welcomeMessage.textContent = `Welcome, ${profile.username || 'User'}!`; - profileContainer.style.display = 'block'; - signInPrompt.style.display = 'none'; - window.document.title = `${profile.username || 'User'}'s Profile - The MovieVerse`; - loadProfile(); - hideSpinner(); + loadProfile(userEmail); + viewMyProfileBtn.disabled = false; + viewMyProfileBtn.style.display = 'block'; } else { - document.getElementById('welcomeMessage').textContent = ''; - document.getElementById('profileContainer').style.display = 'none'; - signInPrompt.textContent = 'Please sign in to view your profile'; - signInPrompt.style.fontWeight = '800'; - signInPrompt.style.color = '#ff8623'; - hideSpinner(); + welcomeMessage.textContent = 'Please sign in to view your profile'; + signInPrompt.style.display = 'block'; + viewMyProfileBtn.disabled = true; + viewMyProfileBtn.style.display = 'none'; + } + + document.getElementById('viewMyProfileBtn').addEventListener('click', () => { + loadCurrentUserProfile(); + }); + + function loadCurrentUserProfile() { + const currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); + if (currentUserEmail) { + loadProfile(currentUserEmail); + } + else { + console.error("No user is currently signed in"); + } + } + + hideSpinner(); +} + +function setupSearchListeners() { + try { + const searchUserInput = document.getElementById('searchUserInput'); + const searchUserResults = document.getElementById('searchUserResults'); + + searchUserInput.addEventListener('input', () => { + const searchText = searchUserInput.value.trim(); + + if (searchText) { + performSearch(searchText); + } + else { + searchUserResults.innerHTML = ''; + searchUserResults.style.display = 'none'; + } + }); + } + catch (error) { + console.error("Error fetching user list: ", error); + if (error.code === 'resource-exhausted') { + const noUserSelected = document.getElementById('profileContainer'); + if (noUserSelected) { + noUserSelected.innerHTML = "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!"; + noUserSelected.style.height = '350px'; + } + } } } -async function loadProfile() { +async function performSearch(searchText) { + const searchUserResults = document.getElementById('searchUserResults'); + const db = getFirestore(); showSpinner(); - const userEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); - if (!userEmail) return; - const docRef = doc(db, 'profiles', userEmail); try { - const docSnap = await getDoc(docRef); - let profile = { - username: 'N/A', - dob: 'N/A', - bio: 'N/A', - favoriteGenres: 'N/A', - location: 'N/A', - favoriteMovie: 'N/A', - hobbies: 'N/A', - favoriteActor: 'N/A', - favoriteDirector: 'N/A', - personalQuote: 'N/A', - profileImage: '../../images/user-default.png' - }; + const userQuery = query(collection(db, 'profiles'), where('username', '>=', searchText), where('username', '<=', searchText + '\uf8ff')); + const querySnapshot = await getDocs(userQuery); - if (docSnap.exists()) { - profile = { ...profile, ...docSnap.data() }; - const imageUrl = profile.profileImage || '../../images/user-default.png'; - document.getElementById('profileImage').src = imageUrl; + searchUserResults.innerHTML = ''; + if (querySnapshot.empty) { + searchUserResults.innerHTML = `
No User with Username "${searchText}" found
`; + searchUserResults.style.display = 'block'; + } + else { + searchUserResults.style.display = 'block'; + querySnapshot.forEach((doc) => { + const user = doc.data(); + const userDiv = document.createElement('div'); + userDiv.className = 'user-search-result'; + userDiv.style.cursor = 'pointer'; + userDiv.addEventListener('click', () => loadProfile(doc.id)); + + const img = document.createElement('img'); + img.src = user.profileImage || '../../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.username}

Bio: ${user.bio || 'Not Set'}

`; + userDiv.appendChild(textDiv); + + searchUserResults.appendChild(userDiv); + }); + } + hideSpinner(); + } + catch (error) { + console.error("Error during search: ", error); + searchUserResults.innerHTML = `
Error in searching: ${error.message}
`; + searchUserResults.style.display = 'block'; + hideSpinner(); + } +} + +document.getElementById('container1').addEventListener('click', async () => { + const userEmail = localStorage.getItem('currentlyViewingProfile'); + + if (!userEmail) { + console.error('No user email found'); + return; + } + + try { + const rating = await getAverageMovieRating(userEmail); + const convertRatingToPercent = (rating / 5) * 100; + const averageRating = convertRatingToPercent.toFixed(1); + + const triviaStats = await getTriviaStats(userEmail); - profile.hobbies = profile.hobbies && profile.hobbies.length > 0 ? profile.hobbies.join(', ') : 'N/A'; + let averageTriviaScore = 0; + if (triviaStats.totalAttempted > 0) { + averageTriviaScore = (triviaStats.totalCorrect / triviaStats.totalAttempted) * 100; } - document.getElementById('profileImage').src = profile.profileImage; - document.getElementById('removeProfileImage').style.display = profile.profileImage && profile.profileImage !== '../../images/user-default.png' ? 'inline' : 'none'; - document.getElementById('usernameDisplay').innerHTML = `Username: ${profile.username}`; - document.getElementById('dobDisplay').innerHTML = `Date of Birth: ${profile.dob}`; - document.getElementById('bioDisplay').innerHTML = `Bio: ${profile.bio}`; - document.getElementById('favoriteGenresDisplay').innerHTML = `Favorite Genres: ${profile.favoriteGenres}`; - document.getElementById('locationDisplay').innerHTML = `Location: ${profile.location}`; - document.getElementById('favoriteMovieDisplay').innerHTML = `Favorite Movie: ${profile.favoriteMovie}`; - document.getElementById('hobbiesDisplay').innerHTML = `Hobbies: ${profile.hobbies}`; - document.getElementById('favoriteActorDisplay').innerHTML = `Favorite Actor: ${profile.favoriteActor}`; - document.getElementById('favoriteDirectorDisplay').innerHTML = `Favorite Director: ${profile.favoriteDirector}`; - document.getElementById('personalQuoteDisplay').innerHTML = `Personal Quote: ${profile.personalQuote}`; - window.document.title = `${profile.username || 'User'}'s Profile - The MovieVerse`; + updateProgressCircles(averageRating, averageTriviaScore, 'container1'); } catch (error) { - console.error("Error loading profile: ", error); + console.error('Error updating progress circles:', error); + } +}); + +document.getElementById('container2').addEventListener('click', async () => { + const userEmail = localStorage.getItem('currentlyViewingProfile'); + + if (!userEmail) { + console.error('No user email found'); + return; + } + + try { + const rating = await getAverageMovieRating(userEmail); + const convertRatingToPercent = (rating / 5) * 100; + const averageRating = convertRatingToPercent.toFixed(1); + + const triviaStats = await getTriviaStats(userEmail); + + let averageTriviaScore = 0; + if (triviaStats.totalAttempted > 0) { + averageTriviaScore = (triviaStats.totalCorrect / triviaStats.totalAttempted) * 100; + } + + updateProgressCircles(averageRating, averageTriviaScore, 'container2'); + } + catch (error) { + console.error('Error updating progress circles:', error); + } +}); + +async function loadProfile(userEmail = localStorage.getItem('currentlySignedInMovieVerseUser')) { + try { + showSpinner(); + + document.getElementById('viewMyProfileBtn').disabled = false; + + if (!userEmail) return; + + const welcomeMessage = document.getElementById('welcomeMessage'); + const profileContainer = document.getElementById('profileContainer'); + const changeProfileImageBtn = document.getElementById('changeProfileImageBtn'); + const editProfileBtn = document.getElementById('editProfileBtn'); + const removeProfileImageBtn = document.getElementById('removeProfileImage'); + const profileImage = document.getElementById('profileImage'); + + if (userEmail !== localStorage.getItem('currentlySignedInMovieVerseUser') || !localStorage.getItem('currentlySignedInMovieVerseUser') || !JSON.parse(localStorage.getItem('isSignedIn'))) { + changeProfileImageBtn.style.display = 'none'; + editProfileBtn.style.display = 'none'; + profileImage.removeAttribute('onclick'); + profileImage.style.cursor = 'default'; + profileImage.title = 'Sign in to change profile image'; + } + else { + changeProfileImageBtn.style.display = ''; + editProfileBtn.style.display = ''; + profileImage.setAttribute('onclick', 'document.getElementById("imageUpload").click()'); + profileImage.style.cursor = 'pointer'; + profileImage.title = 'Click to change profile image'; + } + + profileContainer.style.display = 'block'; + + const docRef = doc(db, 'profiles', userEmail); + const isSignedIn = JSON.parse(localStorage.getItem('isSignedIn')) || false; + const currentUserEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); + + let followUnfollowBtn = document.getElementById('followUnfollowBtn'); + if (!followUnfollowBtn) { + followUnfollowBtn = document.createElement('button'); + followUnfollowBtn.id = 'followUnfollowBtn'; + followUnfollowBtn.style.width = '100%'; + profileContainer.appendChild(followUnfollowBtn); + } + + if (currentUserEmail && userEmail !== currentUserEmail && isSignedIn) { + const followingRef = doc(db, 'profiles', currentUserEmail, 'following', userEmail); + const followersRef = doc(db, 'profiles', userEmail, 'followers', currentUserEmail); + + const followSnap = await getDoc(followingRef); + let isFollowing = followSnap.exists(); + + followUnfollowBtn.textContent = isFollowing ? 'Unfollow' : 'Follow'; + followUnfollowBtn.style.display = 'block'; + + followUnfollowBtn.onclick = async () => { + if (isFollowing) { + await deleteDoc(followingRef); + await deleteDoc(followersRef); + followUnfollowBtn.textContent = 'Follow'; + isFollowing = false; + await displayUserList('followers', userEmail); + } + else { + const timestamp = serverTimestamp(); + await setDoc(followingRef, {timestamp: timestamp}); + await setDoc(followersRef, {timestamp: timestamp}); + followUnfollowBtn.textContent = 'Unfollow'; + isFollowing = true; + await displayUserList('followers', userEmail); + } + }; + } + else { + followUnfollowBtn.style.display = 'none'; + } + + const rating = await getAverageMovieRating(userEmail); + const convertRatingToPercent = (rating / 5) * 100; + const averageRating = convertRatingToPercent.toFixed(1); + + const triviaStats = await getTriviaStats(userEmail); + + let averageTriviaScore = 0; + if (triviaStats.totalAttempted > 0) { + averageTriviaScore = (triviaStats.totalCorrect / triviaStats.totalAttempted) * 100; + } + + localStorage.setItem('currentlyViewingProfile', userEmail); + + updateProgressCircles(averageRating, averageTriviaScore); + + try { + const docSnap = await getDoc(docRef); + let profile = { + username: 'N/A', + dob: 'N/A', + bio: 'N/A', + favoriteGenres: 'N/A', + location: 'N/A', + favoriteMovie: 'N/A', + hobbies: 'N/A', + favoriteActor: 'N/A', + favoriteDirector: 'N/A', + personalQuote: 'N/A', + profileImage: '../../images/user-default.png' + }; + + if (docSnap.exists()) { + profile = {...profile, ...docSnap.data()}; + const imageUrl = profile.profileImage || '../../images/user-default.png'; + document.getElementById('profileImage').src = imageUrl; + + if (userEmail !== localStorage.getItem('currentlySignedInMovieVerseUser') || !localStorage.getItem('currentlySignedInMovieVerseUser') || !JSON.parse(localStorage.getItem('isSignedIn')) || profile.profileImage === '../../images/user-default.png') { + removeProfileImageBtn.style.display = 'none'; + } else { + removeProfileImageBtn.style.display = 'inline'; + } + + document.getElementById('usernameDisplay').innerHTML = `Username: ${profile.username}`; + document.getElementById('dobDisplay').innerHTML = `Date of Birth: ${profile.dob}`; + document.getElementById('bioDisplay').innerHTML = `Bio: ${profile.bio}`; + document.getElementById('favoriteGenresDisplay').innerHTML = `Favorite Genres: ${profile.favoriteGenres}`; + document.getElementById('locationDisplay').innerHTML = `Location: ${profile.location}`; + document.getElementById('favoriteMovieDisplay').innerHTML = `Favorite Movie: ${profile.favoriteMovie}`; + document.getElementById('hobbiesDisplay').innerHTML = `Hobbies: ${profile.hobbies}`; + document.getElementById('favoriteActorDisplay').innerHTML = `Favorite Actor: ${profile.favoriteActor}`; + document.getElementById('favoriteDirectorDisplay').innerHTML = `Favorite Director: ${profile.favoriteDirector}`; + document.getElementById('personalQuoteDisplay').innerHTML = `Personal Quote: ${profile.personalQuote}`; + window.document.title = `${profile.username !== 'N/A' ? profile.username : 'User'}'s Profile - The MovieVerse`; + + if (userEmail === localStorage.getItem('currentlySignedInMovieVerseUser')) { + welcomeMessage.textContent = `Welcome, ${profile.username}!`; + } else { + welcomeMessage.textContent = `Viewing ${profile.username}'s profile`; + } + + await displayUserList('following', userEmail); + await displayUserList('followers', userEmail); + } + else { + const imageUrl = profile.profileImage || '../../images/user-default.png'; + document.getElementById('profileImage').src = imageUrl; + + if (userEmail !== localStorage.getItem('currentlySignedInMovieVerseUser') || !localStorage.getItem('currentlySignedInMovieVerseUser') || !JSON.parse(localStorage.getItem('isSignedIn')) || profile.profileImage === '../../images/user-default.png') { + removeProfileImageBtn.style.display = 'none'; + } + else { + removeProfileImageBtn.style.display = 'inline'; + } + + document.getElementById('usernameDisplay').innerHTML = `Username: N/A`; + document.getElementById('dobDisplay').innerHTML = `Date of Birth: N/A`; + document.getElementById('bioDisplay').innerHTML = `Bio: N/A`; + document.getElementById('favoriteGenresDisplay').innerHTML = `Favorite Genres: N/A`; + document.getElementById('locationDisplay').innerHTML = `Location: N/A`; + document.getElementById('favoriteMovieDisplay').innerHTML = `Favorite Movie: N/A`; + document.getElementById('hobbiesDisplay').innerHTML = `Hobbies: N/A`; + document.getElementById('favoriteActorDisplay').innerHTML = `Favorite Actor: N/A`; + document.getElementById('favoriteDirectorDisplay').innerHTML = `Favorite Director: N/A`; + document.getElementById('personalQuoteDisplay').innerHTML = `Personal Quote: N/A`; + window.document.title = `${profile.username !== 'N/A' ? profile.username : 'User'}'s Profile - The MovieVerse`; + + if (userEmail === localStorage.getItem('currentlySignedInMovieVerseUser')) { + welcomeMessage.textContent = `Welcome, ${profile.username}!`; + } + else { + welcomeMessage.textContent = `Viewing ${profile.username}'s profile`; + } + + await displayUserList('following', userEmail); + await displayUserList('followers', userEmail); + } + } + catch (error) { + if (error.code === 'resource-exhausted') { + const noUserSelected = document.getElementById('profileContainer'); + if (noUserSelected) { + noUserSelected.innerHTML = "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!"; + noUserSelected.style.height = '350px'; + } + hideSpinner(); + } + + document.getElementById('viewMyProfileBtn').disabled = true; + } + + hideSpinner(); + } + catch (error) { + console.error("Error fetching user list: ", error); + if (error.code === 'resource-exhausted') { + const noUserSelected = document.getElementById('profileContainer'); + if (noUserSelected) { + noUserSelected.innerHTML = "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!"; + noUserSelected.style.height = '350px'; + } + hideSpinner(); + } + + document.getElementById('viewMyProfileBtn').disabled = true; + } +} + +async function displayUserList(listType, userEmail) { + showSpinner(); + + const db = getFirestore(); + const listRef = collection(db, 'profiles', userEmail, listType); + const snapshot = await getDocs(listRef); + const userListSpan = document.getElementById(`${listType}List`); + + userListSpan.innerHTML = ''; + + if (snapshot.empty) { + userListSpan.textContent = 'N/A'; + } + else { + for (let docSnapshot of snapshot.docs) { + const userRef = doc(db, 'profiles', docSnapshot.id); + const userSnap = await getDoc(userRef); + if (userSnap.exists()) { + const userData = userSnap.data(); + + const userLink = document.createElement('a'); + userLink.textContent = userData.username; + userLink.href = '#'; + userLink.id = "userLink" + userLink.style.cursor = 'pointer'; + userLink.onclick = () => loadProfile(docSnapshot.id); + + userListSpan.appendChild(userLink); + userListSpan.appendChild(document.createTextNode(', ')); + } + } + + if (userListSpan.lastChild) { + userListSpan.removeChild(userListSpan.lastChild); + } } hideSpinner(); @@ -140,8 +504,19 @@ async function saveProfileChanges() { if (!userEmail) return; const profileRef = doc(db, 'profiles', userEmail); + const currentDoc = await getDoc(profileRef); + const currentProfile = currentDoc.exists() ? currentDoc.data() : null; + + const newUsername = document.getElementById('editUsername').value.trim(); + + if (currentProfile && currentProfile.username && currentProfile.username !== 'N/A' && !newUsername) { + alert("You cannot delete your username. Please enter a valid username."); + document.getElementById('editUsername').value = currentProfile.username; + return; + } + const profile = { - username: document.getElementById('editUsername').value, + username: newUsername || currentProfile.username, dob: document.getElementById('editDob').value, bio: document.getElementById('editBio').value, favoriteGenres: document.getElementById('editFavoriteGenres').value.split(',').map(genre => genre.trim()), @@ -160,7 +535,7 @@ async function saveProfileChanges() { loadProfile(); } catch (error) { - console.error("Error updating profile: ", error); + console.log("Error updating profile: ", error); } } @@ -168,17 +543,18 @@ async function removeProfileImage() { const userEmail = localStorage.getItem('currentlySignedInMovieVerseUser'); if (!userEmail) return; - const profileImageRef = ref(storage, `profileImages/${userEmail}`); + const defaultImageUrl = '../../images/user-default.png'; try { - await deleteObject(profileImageRef); - console.log('File deleted successfully'); - await setDoc(doc(db, 'profiles', userEmail), { profileImage: deleteField() }, { merge: true }); - document.getElementById('profileImage').src = '../../images/user-default.png'; + await setDoc(doc(db, 'profiles', userEmail), { profileImage: defaultImageUrl }, { merge: true }); + + document.getElementById('profileImage').src = defaultImageUrl; document.getElementById('removeProfileImage').style.display = 'none'; + + console.log('Profile image reset to default successfully'); } catch (error) { - console.error("Error removing image: ", error); + console.log("Error removing image: ", error); } } @@ -199,13 +575,14 @@ async function uploadImage() { try { const base64Image = await resizeImageAndConvertToBase64(file, 1024, 1024); - await setDoc(doc(db, 'profiles', userEmail), { profileImageBase64: base64Image }, { merge: true }); - document.getElementById('profileImage').src = base64Image; + await setDoc(doc(db, 'profiles', userEmail), { profileImage: base64Image }, { merge: true }); + document.getElementById('profileImage').src = base64Image; console.log('Image processed and Firestore updated'); + window.location.reload(); } catch (error) { - console.error("Error during image processing:", error); + console.log("Error during image processing:", error); alert('Error during image processing: ' + error.message); } } @@ -269,6 +646,7 @@ function setupEventListeners() { try { const docRef = doc(db, 'profiles', userEmail); const docSnap = await getDoc(docRef); + let profile = { username: 'N/A', dob: '', @@ -307,7 +685,7 @@ function setupEventListeners() { document.getElementById('editProfileModal').style.display = 'block'; } catch (error) { - console.error("Error accessing Firestore: ", error); + console.log("Error accessing Firestore: ", error); } });