diff --git a/MovieVerse-Mobile/app/ios/AboutViewController.swift b/MovieVerse-Mobile/app/ios/AboutViewController.swift new file mode 100644 index 00000000..8c8922b7 --- /dev/null +++ b/MovieVerse-Mobile/app/ios/AboutViewController.swift @@ -0,0 +1,121 @@ +import UIKit + +// MARK: - Data Model +struct AboutSection { + let title: String + let description: String +} + +class AboutViewController: UIViewController { + + // Data + private let sections: [AboutSection] = [ + AboutSection(title: "Welcome to The MovieVerse", + description: "Your ultimate destination for exploring the magic of movies. Here, you can discover, engage, and immerse yourself in the world of film."), + AboutSection(title: "Core Features", + description: "Explore movies by genre, director, language, and era. Enjoy interactive movie details, high-quality trailers, and a community-driven recommendation system."), + AboutSection(title: "Our Vision", + description: "We aim to create a haven for cinephiles, where discovery of movies is an adventure. It's a vibrant community for sharing cinematic experiences and insights."), + AboutSection(title: "Join The Journey", + description: "We're constantly innovating to bring you new and exciting features. Your engagement and feedback are invaluable to us.") + ] + + // UI Components + private lazy var tableView: UITableView = { + let table = UITableView() + table.delegate = self + table.dataSource = self + table.register(AboutSectionCell.self, forCellReuseIdentifier: AboutSectionCell.identifier) + return table + }() + + // MARK: - Lifecycle + override func viewDidLoad() { + super.viewDidLoad() + setupUI() + } + + // MARK: - Setup + private func setupUI() { + view.addSubview(tableView) + tableView.frame = view.bounds + title = "About The MovieVerse" + } +} + +// MARK: - UITableView DataSource & Delegate +extension AboutViewController: UITableViewDataSource, UITableViewDelegate { + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return sections.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: AboutSectionCell.identifier, for: indexPath) as? AboutSectionCell else { + return UITableViewCell() + } + let section = sections[indexPath.row] + cell.configure(with: section) + return cell + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return UITableView.automaticDimension + } +} + +// MARK: - Custom TableViewCell +class AboutSectionCell: UITableViewCell { + + static let identifier = "AboutSectionCell" + + private let titleLabel: UILabel = { + let label = UILabel() + label.font = UIFont.boldSystemFont(ofSize: 18) + label.numberOfLines = 0 + return label + }() + + private let descriptionLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 16) + label.numberOfLines = 0 + label.textColor = .darkGray + return label + }() + + // Initialization + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + contentView.addSubview(titleLabel) + contentView.addSubview(descriptionLabel) + applyConstraints() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // Configure Cell + func configure(with section: AboutSection) { + titleLabel.text = section.title + descriptionLabel.text = section.description + } + + // Constraints + private func applyConstraints() { + titleLabel.translatesAutoresizingMaskIntoConstraints = false + descriptionLabel.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + titleLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10), + titleLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10), + titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10), + + descriptionLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 5), + descriptionLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 10), + descriptionLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -10), + descriptionLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10) + ]) + } +} diff --git a/MovieVerse-Mobile/app/ios/ActorDetailsViewController.swift b/MovieVerse-Mobile/app/ios/ActorDetailsViewController.swift new file mode 100644 index 00000000..5784b266 --- /dev/null +++ b/MovieVerse-Mobile/app/ios/ActorDetailsViewController.swift @@ -0,0 +1,96 @@ +import UIKit + +// MARK: - Actor Data Model +struct Actor { + let id: Int + let name: String + let bio: String + let birthDate: String + let filmography: [String] +} + +class ActorDetailsViewController: UIViewController { + + var actor: Actor? // Actor data passed from the previous screen + + // UI Components + private let nameLabel: UILabel = { + let label = UILabel() + label.font = UIFont.boldSystemFont(ofSize: 24) + label.textAlignment = .center + return label + }() + + private let bioLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 16) + label.numberOfLines = 0 + label.textAlignment = .left + return label + }() + + private let tableView: UITableView = { + let table = UITableView() + table.register(UITableViewCell.self, forCellReuseIdentifier: "cell") + return table + }() + + // MARK: - Lifecycle + override func viewDidLoad() { + super.viewDidLoad() + setupUI() + configureTableView() + loadActorDetails() + } + + // MARK: - Setup + private func setupUI() { + view.backgroundColor = .white + view.addSubview(nameLabel) + view.addSubview(bioLabel) + view.addSubview(tableView) + layoutUI() + } + + private func configureTableView() { + tableView.dataSource = self + tableView.delegate = self + } + + private func layoutUI() { + nameLabel.translatesAutoresizingMaskIntoConstraints = false + bioLabel.translatesAutoresizingMaskIntoConstraints = false + tableView.translatesAutoresizingMaskIntoConstraints = false + dobLabel.translatesAutoresizingMaskIntoConstraints = false + ageLabel.translatesAutoresizingMaskIntoConstraints = false + filmographyLabel.translatesAutoresizingMaskIntoConstraints = false + heightLabel.translatesAutoresizingMaskIntoConstraints = false + } + + private func loadActorDetails() { + guard let actor = actor else { return } + nameLabel.text = actor.name + bioLabel.text = "Biography: \(actor.bio)" + + tableView.reloadData() + } +} + +// MARK: - UITableViewDataSource, UITableViewDelegate +extension ActorDetailsViewController: UITableViewDataSource, UITableViewDelegate { + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return actor?.filmography.count ?? 0 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) + cell.textLabel?.text = actor?.filmography[indexPath.row] + return cell + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + tableView.reloadData() + } +} diff --git a/MovieVerse-Mobile/app/ios/DirectorDetailsViewController.swift b/MovieVerse-Mobile/app/ios/DirectorDetailsViewController.swift new file mode 100644 index 00000000..ba3d1ce5 --- /dev/null +++ b/MovieVerse-Mobile/app/ios/DirectorDetailsViewController.swift @@ -0,0 +1,106 @@ +import UIKit + +// MARK: - Director Data Model +struct Director { + let id: Int + let name: String + let bio: String + let birthDate: String + let filmography: [String] +} + +class DirectorDetailsViewController: UIViewController { + + var directorId: Int? // Director ID passed from the previous screen + + // UI Components + private let nameLabel: UILabel = { + let label = UILabel() + label.font = UIFont.boldSystemFont(ofSize: 24) + label.textAlignment = .center + return label + }() + + private let bioTextView: UITextView = { + let textView = UITextView() + textView.font = UIFont.systemFont(ofSize: 16) + textView.isEditable = false + return textView + }() + + private let tableView: UITableView = { + let table = UITableView() + table.register(UITableViewCell.self, forCellReuseIdentifier: "cell") + return table + }() + + // MARK: - Lifecycle + override func viewDidLoad() { + super.viewDidLoad() + setupUI() + configureTableView() + loadDirectorDetails() + } + + // MARK: - Setup + private func setupUI() { + view.backgroundColor = .white + view.addSubview(nameLabel) + view.addSubview(bioTextView) + view.addSubview(tableView) + layoutUI() + } + + private func configureTableView() { + tableView.dataSource = self + tableView.delegate = self + } + + private func layoutUI() { + // Constraints setup for nameLabel, bioTextView, and tableView + } + + private func loadDirectorDetails() { + guard let directorId = directorId else { return } + fetchDirectorDetails(directorId: directorId) + } + + private func fetchDirectorDetails(directorId: Int) {} + let sampleDirector = Director(id: directorId, name: "Christopher Nolan", bio: "An acclaimed British-American film director...", birthDate: "1970-07-30", filmography: ["Inception", "The Dark Knight", "Interstellar"]) + + nameLabel.text = sampleDirector.name + bioTextView.text = "Bio: \(sampleDirector.bio)\n\nBorn: \(sampleDirector.birthDate)" + + apiClient.fetchDirectorFilmography(directorId: directorId) { result in + switch result { + case .success(let movies): + director.filmography = movies.map { $0.title } + case .failure(let error): + print(error.localizedDescription) + } + } + + resultsLabel.text = "Filmography" + + tableView.reloadData() + } +} + +// MARK: - UITableViewDataSource, UITableViewDelegate +extension DirectorDetailsViewController: UITableViewDataSource, UITableViewDelegate { + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return 3 // Sample data + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) + cell.textLabel?.text = ["Inception", "The Dark Knight", "Interstellar"][indexPath.row] + return cell + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + movieDetails(movie: ["Inception", "The Dark Knight", "Interstellar"][indexPath.row]) + } +} diff --git a/MovieVerse-Mobile/app/ios/MovieDetailViewController.swift b/MovieVerse-Mobile/app/ios/MovieDetailViewController.swift index 7a238d28..6d6c2354 100644 --- a/MovieVerse-Mobile/app/ios/MovieDetailViewController.swift +++ b/MovieVerse-Mobile/app/ios/MovieDetailViewController.swift @@ -442,4 +442,4 @@ struct MoviesViewController: UIViewController, UITableViewDelegate, UITableViewD movieDetailViewController.movie = movie } } -} \ No newline at end of file +} diff --git a/MovieVerse-Mobile/app/ios/MovieDetailsView.swift b/MovieVerse-Mobile/app/ios/MovieDetailsView.swift index 7eb4fcc5..e49be084 100644 --- a/MovieVerse-Mobile/app/ios/MovieDetailsView.swift +++ b/MovieVerse-Mobile/app/ios/MovieDetailsView.swift @@ -72,7 +72,7 @@ struct MoviePosterImage: View { }.resume() } - private func fetchImageData() { + private func fetchImageData2() { URLSession.shared.dataTaskPublisher(for: url) .map { $0.data } .replaceError(with: nil) diff --git a/MovieVerse-Mobile/app/ios/MovieTimelineViewController.swift b/MovieVerse-Mobile/app/ios/MovieTimelineViewController.swift new file mode 100644 index 00000000..b9f89e33 --- /dev/null +++ b/MovieVerse-Mobile/app/ios/MovieTimelineViewController.swift @@ -0,0 +1,203 @@ +import UIKit + +// MARK: - Data Model +struct MovieTimelineSection { + let period: String + let movies: [Movie] +} + +struct Movie { + let title: String + let releaseYear: String + let description: String + let posterURL: String +} + +class MovieTimelineViewController: UIViewController { + + // Sample Data + private let timelineSections: [MovieTimelineSection] = [ + MovieTimelineSection(period: "1920s", movies: [Movie(title: "Movie 1", releaseYear: "1920", description: "Description 1", posterURL: "URL1")]), + MovieTimelineSection(period: "1930s", movies: [Movie(title: "Movie 2", releaseYear: "1930", description: "Description 2", posterURL: "URL2")]), + MovieTimelineSection(period: "1940s", movies: [Movie(title: "Movie 3", releaseYear: "1940", description: "Description 3", posterURL: "URL3")]), + MovieTimelineSection(period: "1950s", movies: [Movie(title: "Movie 4", releaseYear: "1950", description: "Description 4", posterURL: "URL4")]), + MovieTimelineSection(period: "1960s", movies: [Movie(title: "Movie 5", releaseYear: "1960", description: "Description 5", posterURL: "URL5")]), + MovieTimelineSection(period: "1970s", movies: [Movie(title: "Movie 6", releaseYear: "1970", description: "Description 6", posterURL: "URL6")]), + MovieTimelineSection(period: "1980s", movies: [Movie(title: "Movie 7", releaseYear: "1980", description: "Description 7", posterURL: "URL7")]), + MovieTimelineSection(period: "1990s", movies: [Movie(title: "Movie 8", releaseYear: "1990", description: "Description 8", posterURL: "URL8")]), + MovieTimelineSection(period: "2000s", movies: [Movie(title: "Movie 9", releaseYear: "2000", description: "Description 9", posterURL: "URL9")]), + MovieTimelineSection(period: "2010s", movies: [Movie(title: "Movie 10", releaseYear: "2010", description: "Description 10", posterURL: "URL10")]), + MovieTimelineSection(period: "2020s", movies: [Movie(title: "Movie 11", releaseYear: "2020", description: "Description 11", posterURL: "URL11")]) + ] + + // UI Components + private lazy var tableView: UITableView = { + let table = UITableView() + table.delegate = self + table.dataSource = self + table.register(MovieTimelineCell.self, forCellReuseIdentifier: MovieTimelineCell.identifier) + return table + }() + + // MARK: - Lifecycle + override func viewDidLoad() { + super.viewDidLoad() + setupUI() + } + + // MARK: - Setup + private func setupUI() { + view.addSubview(tableView) + tableView.frame = view.bounds + title = "Movie Timeline" + } + + // MARK: - Reuse + override func prepareForReuse() { + super.prepareForReuse() + tableView.reloadData() + } +} + +// MARK: - UITableView DataSource & Delegate +extension MovieTimelineViewController: UITableViewDataSource, UITableViewDelegate { + + func numberOfSections(in tableView: UITableView) -> Int { + return timelineSections.count + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return timelineSections[section].movies.count + } + + func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + return timelineSections[section].period + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: MovieTimelineCell.identifier, for: indexPath) as? MovieTimelineCell else { + return UITableViewCell() + } + let movie = timelineSections[indexPath.section].movies[indexPath.row] + cell.configure(with: movie) + return cell + } + + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 120 + } + + func sectionIndexTitles(for tableView: UITableView) -> [String]? { + return timelineSections.map { $0.period } + } + + func tableView(_ tableView: UITableView, sectionForSectionIndexTitle title: String, at index: Int) -> Int { + return timelineSections.firstIndex { $0.period == title } ?? 0 + } +} + +// MARK: - Custom TableViewCell +class MovieTimelineCell: UITableViewCell { + + static let identifier = "MovieTimelineCell" + + // UI Elements for the cell + private let titleLabel: UILabel = { + let label = UILabel() + label.font = UIFont.boldSystemFont(ofSize: 18) + return label + }() + + private let yearLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 16) + label.textColor = .gray + return label + }() + + private let descriptionLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 16) + label.numberOfLines = 0 + return label + }() + + private let posterImageView: UIImageView = { + let imageView = UIImageView() + imageView.contentMode = .scaleAspectFit + imageView.clipsToBounds = true + return imageView + }() + + // Initialization + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { + super.init(style: style, reuseIdentifier: reuseIdentifier) + setupLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + // Configure Cell + func configure(with movie: Movie) { + titleLabel.text = movie.title + yearLabel.text = "Release Year: \(movie.releaseYear)" + descriptionLabel.text = movie.description + posterImageView.image = UIImage(named: movie.posterURL) // replace with actual image loading logic + } + + // Layout + private func setupLayout() { + let stackView = UIStackView(arrangedSubviews: [titleLabel, yearLabel, descriptionLabel]) + stackView.axis = .vertical + stackView.spacing = 4 + stackView.translatesAutoresizingMaskIntoConstraints = false + + addSubview(posterImageView) + addSubview(stackView) + + NSLayoutConstraint.activate([ + posterImageView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10), + posterImageView.topAnchor.constraint(equalTo: topAnchor, constant: 10), + posterImageView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10), + posterImageView.widthAnchor.constraint(equalToConstant: 100), + + stackView.leadingAnchor.constraint(equalTo: posterImageView.trailingAnchor, constant: 10), + stackView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10), + stackView.topAnchor.constraint(equalTo: topAnchor, constant: 10), + stackView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10) + ]) + } + + private func applyConstraints() { + titleLabel.translatesAutoresizingMaskIntoConstraints = false + yearLabel.translatesAutoresizingMaskIntoConstraints = false + descriptionLabel.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + titleLabel.topAnchor.constraint(equalTo: topAnchor, constant: 10), + titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10), + titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10), + + yearLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 5), + yearLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10), + yearLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10), + + descriptionLabel.topAnchor.constraint(equalTo: yearLabel.bottomAnchor, constant: 5), + descriptionLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10), + descriptionLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10), + descriptionLabel.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -10) + ]) + } + + // MARK: - Reuse + override func prepareForReuse() { + super.prepareForReuse() + titleLabel.text = nil + yearLabel.text = nil + descriptionLabel.text = nil + posterImageView.image = nil + } + +} diff --git a/MovieVerse-Mobile/app/ios/MovieTriviaViewController.swift b/MovieVerse-Mobile/app/ios/MovieTriviaViewController.swift new file mode 100644 index 00000000..933f8caa --- /dev/null +++ b/MovieVerse-Mobile/app/ios/MovieTriviaViewController.swift @@ -0,0 +1,186 @@ +import UIKit + +// MARK: - Data Model +struct TriviaQuestion { + let question: String + let options: [String] + let correctAnswer: String +} + +class MovieTriviaViewController: UIViewController { + +// Data in 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" }, +// { question: "What was the first feature-length animated movie ever released?", options: ["Snow White and the Seven Dwarfs", "Bambi", "Pinocchio"], answer: "Snow White and the Seven Dwarfs" }, +// { question: "What was the highest-grossing movie of all time before the release of 'Avatar'?", options: ["Titanic", "Star Wars: The Force Awakens", "Avengers: Endgame"], answer: "Titanic" }, +// { question: "Who played the lead role in the movie 'Forrest Gump'?", options: ["Tom Hanks", "Brad Pitt", "Leonardo DiCaprio"], answer: "Tom Hanks" }, +// { question: "What movie won the Academy Award for Best Picture in 2019?", options: ["Bohemian Rhapsody", "Green Book", "Roma"], answer: "Green Book" }, +// { question: "Who played the character of John McClane in the Die Hard movie series?", options: ["Arnold Schwarzenegger", "Sylvester Stallone", "Bruce Willis"], answer: "Bruce Willis" }, +// { question: "What movie is based on a novel by Stephen King and features a character named Jack Torrance?", options: ["Carrie", "The Shining", "Misery"], answer: "The Shining" }, +// { question: "Who directed the movie 'Forrest Gump'?", options: ["Steven Spielberg", "Robert Zemeckis", "Martin Scorsese"], answer: "Robert Zemeckis" }, +// { question: "What is the highest-grossing movie of all time (as of 2021)?", options: ["Avatar", "Avengers: Endgame", "Titanic"], answer: "Avatar" }, +// { question: "What movie features the line 'You can't handle the truth!'?", options: ["The Shawshank Redemption", "A Few Good Men", "Goodfellas"], answer: "A Few Good Men" }, +// { question: "Who played the character of Tony Stark/Iron Man in the Marvel Cinematic Universe?", options: ["Chris Hemsworth", "Mark Ruffalo", "Robert Downey Jr."], answer: "Robert Downey Jr." }, +// { question: "In which movie did Tom Hanks say, 'Houston, we have a problem'?", options: ["Apollo 13", "Cast Away", "The Terminal"], answer: "Apollo 13" }, +// { question: "What is the name of the hobbit played by Elijah Wood in the Lord of the Rings movies?", options: ["Frodo", "Sam", "Merry"], answer: "Frodo" }, +// { question: "What is the name of the kingdom where the 2013 animated movie 'Frozen' is set?", options: ["Arendelle", "Genovia", "DunBroch"], answer: "Arendelle" }, +// { question: "Which 1997 science fiction movie stars Will Smith and Tommy Lee Jones?", options: ["Independence Day", "Men in Black", "Wild Wild West"], answer: "Men in Black" }, +// { question: "Which movie features Bruce Willis as a child psychologist?", options: ["Die Hard", "The Sixth Sense", "Unbreakable"], answer: "The Sixth Sense" }, +// { question: "In 'The Matrix', does Neo take the blue pill or the red pill?", options: ["Blue", "Red", "Green"], answer: "Red" }, +// { question: "Which actress played Katniss Everdeen in 'The Hunger Games' movies?", options: ["Jennifer Lawrence", "Emma Watson", "Shailene Woodley"], answer: "Jennifer Lawrence" }, +// { question: "Who directed 'Jurassic Park'?", options: ["James Cameron", "Steven Spielberg", "George Lucas"], answer: "Steven Spielberg" }, +// { question: "What 1980s movie was the highest grossing film of the decade?", options: ["E.T. the Extra-Terrestrial", "Star Wars: Episode V", "Back to the Future"], answer: "E.T. the Extra-Terrestrial" }, +// { question: "Which movie features the song 'My Heart Will Go On'?", options: ["The Bodyguard", "Titanic", "Romeo + Juliet"], answer: "Titanic" }, +// { question: "What was the first Pixar movie?", options: ["Toy Story", "A Bug's Life", "Monsters, Inc."], answer: "Toy Story" }, +// { question: "Who played Wolverine in the X-Men movies?", options: ["Hugh Jackman", "Liam Hemsworth", "Chris Evans"], answer: "Hugh Jackman" }, +// { question: "Which film did NOT win the Academy Award for Best Picture?", options: ["The Shawshank Redemption", "The Godfather", "Forrest Gump"], answer: "The Shawshank Redemption" }, +// { question: "What is Indiana Jones' real first name?", options: ["Henry", "John", "Walter"], answer: "Henry" }, +// { question: "In 'The Wizard of Oz', what did the Scarecrow want from the wizard?", options: ["Heart", "Brain", "Courage"], answer: "Brain" }, +// { question: "Who is the only actor to receive an Oscar nomination for acting in a Lord of the Rings movie?", options: ["Viggo Mortensen", "Ian McKellen", "Elijah Wood"], answer: "Ian McKellen" }, +// { question: "Which movie features an iconic dance scene between Uma Thurman and John Travolta?", options: ["Pulp Fiction", "Kill Bill", "Saturday Night Fever"], answer: "Pulp Fiction" }, +// { question: "What is the highest-grossing R-rated movie of all time?", options: ["Deadpool", "Joker", "The Matrix"], answer: "Joker" }, +// { question: "Which Alfred Hitchcock movie is notorious for its shower scene?", options: ["Vertigo", "Psycho", "Rear Window"], answer: "Psycho" }, +// { question: "What is Darth Vader's real name?", options: ["Anakin Skywalker", "Luke Skywalker", "Obi-Wan Kenobi"], answer: "Anakin Skywalker" }, +// { question: "Who directed 'Schindler's List'?", options: ["Martin Scorsese", "Steven Spielberg", "Ridley Scott"], answer: "Steven Spielberg" }, +// { question: "In which movie does Tom Cruise perform his own stunts climbing the Burj Khalifa?", options: ["Mission: Impossible - Rogue Nation", "Mission: Impossible - Ghost Protocol", "Edge of Tomorrow"], answer: "Mission: Impossible - Ghost Protocol" }, +// { question: "What is the name of the fictional African country where 'Black Panther' is set?", options: ["Wakanda", "Genovia", "Zamunda"], answer: "Wakanda" }, +// { question: "Who directed 'Inception' and 'Interstellar'?", options: ["Christopher Nolan", "James Cameron", "Steven Spielberg"], answer: "Christopher Nolan" }, +// { question: "In 'The Hunger Games', what district do Katniss and Peeta represent?", options: ["District 12", "District 9", "District 7"], answer: "District 12" }, +// { question: "Which movie features a character named Tyler Durden?", options: ["Fight Club", "Gone Girl", "Seven"], answer: "Fight Club" }, +// { question: "What is the name of the island in 'Jurassic Park'?", options: ["Isla Nublar", "Isla Sorna", "Skull Island"], answer: "Isla Nublar" }, +// { question: "Who played the Joker in 'The Dark Knight'?", options: ["Heath Ledger", "Joaquin Phoenix", "Jared Leto"], answer: "Heath Ledger" }, +// { question: "In which movie is the fictional company Initech featured?", options: ["Office Space", "The Social Network", "Wall Street"], answer: "Office Space" }, +// { question: "What year was the first 'Harry Potter' movie released?", options: ["2001", "2003", "1999"], answer: "2001" }, +// { question: "What fictional country is 'Wonder Woman' from?", options: ["Themyscira", "Asgard", "Genovia"], answer: "Themyscira" }, +// { question: "Which movie is known for the quote 'Here's looking at you, kid'?", options: ["Casablanca", "Gone with the Wind", "The Maltese Falcon"], answer: "Casablanca" }, +// { question: "In 'The Lion King', what is Simba's mother's name?", options: ["Nala", "Sarabi", "Shenzi"], answer: "Sarabi" }, +// { question: "Who directed 'Avengers: Endgame'?", options: ["The Russo Brothers", "Joss Whedon", "Jon Favreau"], answer: "The Russo Brothers" }, +// { question: "What is the name of the kingdom in 'Tangled'?", options: ["Corona", "Far Far Away", "Arendelle"], answer: "Corona" }, +// { question: "Which film features a famous dance scene with Uma Thurman and John Travolta?", options: ["Pulp Fiction", "Saturday Night Fever", "Kill Bill"], answer: "Pulp Fiction" }, +// { question: "Who played Jack Dawson in 'Titanic'?", options: ["Leonardo DiCaprio", "Brad Pitt", "Johnny Depp"], answer: "Leonardo DiCaprio" }, +// { question: "What is the highest-grossing film of all time?", options: ["Avengers: Endgame", "Avatar", "Titanic"], answer: "Avatar" }, +// { question: "In which movie does the character Neo appear?", options: ["The Matrix", "John Wick", "Speed"], answer: "The Matrix" }, +// { question: "What is the real name of the Black Panther in the Marvel Cinematic Universe?", options: ["T'Challa", "M'Baku", "N'Jadaka"], answer: "T'Challa" }, +// { question: "Who directed 'Mad Max: Fury Road'?", options: ["George Miller", "Ridley Scott", "Peter Jackson"], answer: "George Miller" }, +// { question: "What animated film features a character named 'Hiccup'?", options: ["Brave", "How to Train Your Dragon", "Shrek"], answer: "How to Train Your Dragon" }, +// { question: "In which film is the fictional mineral 'Unobtainium' sought after?", options: ["Avatar", "The Core", "Transformers"], answer: "Avatar" }, +// ]; + + // Trivia Data + private var questions: [TriviaQuestion] = [ + TriviaQuestion(question: "What movie won the Academy Award for Best Picture in 2020?", options: ["Joker", "1917", "Parasite"], correctAnswer: "Parasite"), + TriviaQuestion(question: "Who directed the movie 'The Godfather'?", options: ["Steven Spielberg", "Francis Ford Coppola", "Martin Scorsese"], correctAnswer: "Francis Ford Coppola"), + TriviaQuestion(question: "What was the first feature-length animated movie ever released?", options: ["Snow White and the Seven Dwarfs", "Bambi", "Pinocchio"], correctAnswer: "Snow White and the Seven Dwarfs"), + TriviaQuestion(question: "What was the highest-grossing movie of all time before the release of 'Avatar'?", options: ["Titanic", "Star Wars: The Force Awakens", "Avengers: Endgame"], correctAnswer: "Titanic"), + TriviaQuestion(question: "Who played the lead role in the movie 'Forrest Gump'?", options: ["Tom Hanks", "Brad Pitt", "Leonardo DiCaprio"], correctAnswer: "Tom Hanks"), + TriviaQuestion(question: "What movie won the Academy Award for Best Picture in 2019?", options: ["Bohemian Rhapsody", "Green Book", "Roma"], correctAnswer: "Green Book"), + TriviaQuestion(question: "Who played the character of John McClane in the Die Hard movie series?", options: ["Arnold Schwarzenegger", "Sylvester Stallone", "Bruce Willis"], correctAnswer: "Bruce Willis"), + TriviaQuestion(question: "What movie is based on a novel by Stephen King and features a character named Jack Torrance?", options: ["Carrie", "The Shining", "Misery"], correctAnswer: "The Shining"), + TriviaQuestion(question: "Who directed the movie 'Forrest Gump'?", options: ["Steven Spielberg", "Robert Zemeckis", "Martin Scorsese"], correctAnswer: "Robert Zemeckis"), + TriviaQuestion(question: "What is the highest-grossing movie of all time (as of 2021)?", options: ["Avatar", "Avengers: Endgame", "Titanic"], correctAnswer: "Avatar"), + ] + + private var currentQuestionIndex = 0 + private var score = 0 + + // UI Components + private let questionLabel: UILabel = { + let label = UILabel() + label.font = UIFont.systemFont(ofSize: 20) + label.numberOfLines = 0 + label.textAlignment = .center + return label + }() + + private var optionButtons: [UIButton] = [] + + // MARK: - Lifecycle + override func viewDidLoad() { + super.viewDidLoad() + setupUI() + loadQuestion() + } + + // MARK: - Setup + private func setupUI() { + view.backgroundColor = .white + view.addSubview(questionLabel) + + setupOptionButtons() + layoutUI() + title = "Movie Trivia" + } + + private func setupOptionButtons() { + for _ in 1...4 { + let button = UIButton(type: .system) + button.titleLabel?.font = UIFont.systemFont(ofSize: 18) + button.addTarget(self, action: #selector(optionButtonTapped), for: .touchUpInside) + optionButtons.append(button) + view.addSubview(button) + } + } + + private func layoutUI() { + questionLabel.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + questionLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20), + questionLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), + questionLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20) + ]) + + var previousButton: UIButton? + for button in optionButtons { + button.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + button.topAnchor.constraint(equalTo: previousButton?.bottomAnchor ?? questionLabel.bottomAnchor, constant: 20), + button.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20), + button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20) + ]) + previousButton = button + } + } + + // MARK: - Actions + @objc private func optionButtonTapped(_ sender: UIButton) { + guard let answer = sender.titleLabel?.text else { return } + checkAnswer(answer) + } + + // MARK: - Trivia Logic + private func loadQuestion() { + guard currentQuestionIndex < questions.count else { + // Show results + showResults() + return + } + + let currentQuestion = questions[currentQuestionIndex] + questionLabel.text = currentQuestion.question + + for (index, button) in optionButtons.enumerated() { + button.setTitle(currentQuestion.options[index], for: .normal) + } + } + + private func checkAnswer(_ answer: String) { + let currentQuestion = questions[currentQuestionIndex] + if answer == currentQuestion.correctAnswer { + score += 1 + } + + currentQuestionIndex += 1 + loadQuestion() + } + + private func showResults() { + let alertController = UIAlertController(title: "Trivia Complete", message: "Your score: \(score)/\(questions.count)", preferredStyle: .alert) + let action = UIAlertAction(title: "OK", style: .default) { _ in + self.navigationController?.popViewController(animated: true) + } + alertController.addAction(action) + present(alertController, animated: true) + } +} diff --git a/MovieVerse-Mobile/app/ios/SearchViewController.swift b/MovieVerse-Mobile/app/ios/SearchViewController.swift new file mode 100644 index 00000000..08811dbe --- /dev/null +++ b/MovieVerse-Mobile/app/ios/SearchViewController.swift @@ -0,0 +1,90 @@ +import UIKit + +struct Movie { + let title: String + let releaseYear: String + let description: String + let posterURL: String +} + +class SearchViewController: UIViewController, UISearchBarDelegate { + + private let searchBar: UISearchBar = { + let searchBar = UISearchBar() + searchBar.placeholder = "Search Movies" + return searchBar + }() + + private var movies: [Movie] = [] + + private let tableView: UITableView = { + let table = UITableView() + table.register(UITableViewCell.self, forCellReuseIdentifier: "cell") + return table + }() + + // MARK: - Lifecycle + override func viewDidLoad() { + super.viewDidLoad() + setupUI() + configureTableView() + } + + // MARK: - Setup + private func setupUI() { + view.backgroundColor = .white + searchBar.delegate = self + navigationItem.titleView = searchBar + view.addSubview(tableView) + layoutUI() + } + + private func configureTableView() { + tableView.dataSource = self + tableView.delegate = self + } + + private func layoutUI() { + tableView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0), + tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0), + tableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 0), + tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 0) + ]) + searchedMoviesLabel.translatesAutoresizingMaskIntoConstraints = false + resultsLabel.translatesAutoresizingMaskIntoConstraints = false + } + + // MARK: - UISearchBarDelegate + func searchBarSearchButtonClicked(_ searchBar: UISearchBar) { + guard let query = searchBar.text, !query.isEmpty else { return } + searchMovies(query: query) + } + + private func searchMovies(query: String) { + tableView.isHidden = false + movies = [] // Reset movies + movies = MovieService.shared.searchMovies(query: query) + tableView.reloadData() + } +} + +// MARK: - UITableViewDataSource, UITableViewDelegate +extension SearchViewController: UITableViewDataSource, UITableViewDelegate { + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return movies.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) + cell.textLabel?.text = movies[indexPath.row].title + return cell + } + + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + tableView.deselectRow(at: indexPath, animated: true) + movieDetails(movie: movies[indexPath.row]) + } +} diff --git a/MovieVerse-Mobile/platforms/ios/CordovaLib/CordovaLib.xcodeproj/xcuserdata/davidnguyen.xcuserdatad/xcschemes/xcschememanagement.plist b/MovieVerse-Mobile/platforms/ios/CordovaLib/CordovaLib.xcodeproj/xcuserdata/davidnguyen.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..33ba33c2 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/CordovaLib/CordovaLib.xcodeproj/xcuserdata/davidnguyen.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,19 @@ + + + + + SchemeUserState + + Cordova.xcscheme_^#shared#^_ + + orderHint + 2 + + CordovaLib.xcscheme_^#shared#^_ + + orderHint + 1 + + + + diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse.xcodeproj/project.xcworkspace/xcuserdata/davidnguyen.xcuserdatad/UserInterfaceState.xcuserstate b/MovieVerse-Mobile/platforms/ios/MovieVerse.xcodeproj/project.xcworkspace/xcuserdata/davidnguyen.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index c4cf6050..00000000 Binary files a/MovieVerse-Mobile/platforms/ios/MovieVerse.xcodeproj/project.xcworkspace/xcuserdata/davidnguyen.xcuserdatad/UserInterfaceState.xcuserstate and /dev/null differ diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse.xcworkspace/xcuserdata/davidnguyen.xcuserdatad/UserInterfaceState.xcuserstate b/MovieVerse-Mobile/platforms/ios/MovieVerse.xcworkspace/xcuserdata/davidnguyen.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 00000000..a395af29 Binary files /dev/null and b/MovieVerse-Mobile/platforms/ios/MovieVerse.xcworkspace/xcuserdata/davidnguyen.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/MovieVerse-Mobile/platforms/ios/MovieVerse.xcodeproj/xcuserdata/davidnguyen.xcuserdatad/xcschemes/xcschememanagement.plist b/MovieVerse-Mobile/platforms/ios/MovieVerse.xcworkspace/xcuserdata/davidnguyen.xcuserdatad/xcschemes/xcschememanagement.plist similarity index 93% rename from MovieVerse-Mobile/platforms/ios/MovieVerse.xcodeproj/xcuserdata/davidnguyen.xcuserdatad/xcschemes/xcschememanagement.plist rename to MovieVerse-Mobile/platforms/ios/MovieVerse.xcworkspace/xcuserdata/davidnguyen.xcuserdatad/xcschemes/xcschememanagement.plist index 880664ff..e72660e6 100644 --- a/MovieVerse-Mobile/platforms/ios/MovieVerse.xcodeproj/xcuserdata/davidnguyen.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/MovieVerse-Mobile/platforms/ios/MovieVerse.xcworkspace/xcuserdata/davidnguyen.xcuserdatad/xcschemes/xcschememanagement.plist @@ -7,7 +7,7 @@ MovieVerse.xcscheme_^#shared#^_ orderHint - 2 + 0 diff --git a/MovieVerse-Mobile/platforms/ios/www/movie-timeline.html b/MovieVerse-Mobile/platforms/ios/www/movie-timeline.html new file mode 100644 index 00000000..f9afa9a7 --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/www/movie-timeline.html @@ -0,0 +1,53 @@ + + + + + Movie Timeline + + + + + + +
+

+ The Movie Timeline +

+ + +
+ + +
+
+
+
+

Select a Period

+
+ + + +
+ +
+
+ +
+
+ + + + + + + + + + + + diff --git a/MovieVerse-Mobile/platforms/ios/www/movie-timeline.js b/MovieVerse-Mobile/platforms/ios/www/movie-timeline.js new file mode 100644 index 00000000..d206acbb --- /dev/null +++ b/MovieVerse-Mobile/platforms/ios/www/movie-timeline.js @@ -0,0 +1,191 @@ +document.getElementById('load-movies').addEventListener('click', updateMovies); +document.getElementById('start-year').addEventListener('keypress', function(event) { + if (event.keyCode === 13) { + updateMovies(); + } +}); + +document.getElementById('end-year').addEventListener('keypress', function(event) { + if (event.keyCode === 13) { + updateMovies(); + } +}); + +const IMGPATH = "https://image.tmdb.org/t/p/w1280"; +const SEARCHPATH = "https://api.themoviedb.org/3/search/movie?&api_key=c5a20c861acf7bb8d9e987dcc7f1b558&query="; +const searchTitle = document.getElementById("select-text"); +const searchButton = document.getElementById("button-search"); +const search = document.getElementById("search"); +const main = document.getElementById("movies-container"); + +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); + } + allMovies.sort((a, b) => b.vote_average - a.vote_average); + if (allMovies.length > 0) { + showMovies(allMovies.slice(0, numberOfMovies), main); + } + else { + main.innerHTML = `

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

`; + } +} + +function clearMovieDetails() { + const movieDetailsContainer = document.getElementById('movie-details-container'); + if (movieDetailsContainer) { + movieDetailsContainer.innerHTML = ''; + } +} + +form.addEventListener('submit', (e) => { + e.preventDefault(); + const searchTerm = search.value.trim(); + if (searchTerm) { + getMovies(SEARCHPATH + searchTerm); + searchTitle.innerHTML = 'Search Results for: ' + searchTerm; + search.value = ''; + } + else { + searchTitle.innerHTML = 'Please enter a search term.'; + } + document.getElementById('clear-search-btn').style.display = 'block'; +}); + +searchButton.addEventListener('click', (e) => { + e.preventDefault(); + const searchTerm = search.value; + if (searchTerm) { + getMovies(SEARCHPATH + searchTerm); + searchTitle.innerHTML = 'Search Results for: ' + searchTerm; + search.value = ''; + } + else { + searchTitle.innerHTML = 'Please enter a search term.'; + } + document.getElementById('clear-search-btn').style.display = 'block'; +}); + +function updateMovies() { + let startYear = document.getElementById('start-year').value; + let endYear = document.getElementById('end-year').value; + let currentYear = new Date().getFullYear(); + if (startYear && endYear && startYear <= endYear && endYear <= currentYear) { + fetchMoviesByTimePeriod(startYear, endYear); + } + else { + alert('Please ensure the start year is before the end year, and both are not later than the current year.'); + } +} + +function showMovies(movies, mainElement) { + mainElement.innerHTML = ''; + movies.forEach(movie => { + const movieEl = document.createElement('div'); + movieEl.classList.add('movie'); + const movieImage = movie.poster_path + ? `${movie.title}` + : `
Image Not Available
`; + const voteAvg = movie.vote_average.toFixed(1); + const ratingClass = getClassByRate(movie.vote_average); + movieEl.innerHTML = ` + ${movieImage} +
+

${movie.title}

+ ${voteAvg} +
+
+

Movie Intro:

+ ${movie.overview} +
`; + movieEl.addEventListener('click', () => { + localStorage.setItem('selectedMovieId', movie.id); + window.location.href = 'movie-details.html'; + }); + movieEl.style.cursor = 'pointer'; + mainElement.appendChild(movieEl); + }); +} + +document.getElementById('clear-search-btn').addEventListener('click', () => { + location.reload(); +}); + +async function fetchMoviesByTimePeriod(startYear, endYear) { + const url = `https://api.themoviedb.org/3/discover/movie?api_key=c5a20c861acf7bb8d9e987dcc7f1b558&primary_release_date.gte=${startYear}-01-01&primary_release_date.lte=${endYear}-12-31`; + const response = await fetch(url); + const data = await response.json(); + const numberOfMovies = calculateMoviesToDisplay(); + const moviesToShow = data.results.slice(0, numberOfMovies); + showMovies(moviesToShow, document.getElementById('movies-container')); +} + +document.getElementById('load-movies').addEventListener('click', () => { + const startYear = document.getElementById('start-year').value; + const endYear = document.getElementById('end-year').value; + fetchMoviesByTimePeriod(startYear, endYear); +}); + +function calculateMoviesToDisplay() { + const screenWidth = window.innerWidth; + if (screenWidth <= 689.9) return 10; // 1 movie per row + if (screenWidth <= 1021.24) return 20; // 2 movies per row + if (screenWidth <= 1353.74) return 21; // 3 movies per row + if (screenWidth <= 1684.9) return 20; // 4 movies per row + if (screenWidth <= 2017.49) return 20; // 5 movies per row + if (screenWidth <= 2349.99) return 18; // 6 movies per row + if (screenWidth <= 2681.99) return 21; // 7 movies per row + if (screenWidth <= 3014.49) return 24; // 8 movies per row + if (screenWidth <= 3345.99) return 27; // 9 movies per row + if (screenWidth <= 3677.99) return 20; // 10 movies per row + if (screenWidth <= 4009.99) return 22; // 11 movies per row + if (screenWidth <= 4340.99) return 24; // 12 movies per row + if (screenWidth <= 4673.49) return 26; // 13 movies per row + if (screenWidth <= 5005.99) return 28; // 14 movies per row + if (screenWidth <= 5337.99) return 30; // 15 movies per row + if (screenWidth <= 5669.99) return 32; // 16 movies per row + if (screenWidth <= 6001.99) return 34; // 17 movies per row + if (screenWidth <= 6333.99) return 36; // 18 movies per row + if (screenWidth <= 6665.99) return 38; // 19 movies per row + if (screenWidth <= 6997.99) return 40; // 20 movies per row + if (screenWidth <= 7329.99) return 42; // 21 movies per row + if (screenWidth <= 7661.99) return 44; // 22 movies per row + if (screenWidth <= 7993.99) return 46; // 23 movies per row + if (screenWidth <= 8325.99) return 48; // 24 movies per row + return 20; +} + +function getClassByRate(vote) { + if (vote >= 8) { + return 'green'; + } + else if (vote >= 5) { + return 'orange'; + } + else { + return 'red'; + } +} + +async function showMovieOfTheDay(){ + const year = new Date().getFullYear(); + const url = `https://api.themoviedb.org/3/discover/movie?api_key=c5a20c861acf7bb8d9e987dcc7f1b558&sort_by=vote_average.desc&vote_count.gte=100&primary_release_year=${year}&vote_average.gte=7`; + try { + const response = await fetch(url); + const data = await response.json(); + const movies = data.results; + const randomMovie = movies[Math.floor(Math.random() * movies.length)]; + localStorage.setItem('selectedMovieId', randomMovie.id); + window.location.href = 'movie-details.html'; + } + catch (error) { + console.error('Error fetching movie:', error); + alert('Failed to fetch the movie of the day. Please try again later.'); + } +} diff --git a/MovieVerse-Mobile/www/about.html b/MovieVerse-Mobile/www/about.html index d1de90eb..1acf696a 100644 --- a/MovieVerse-Mobile/www/about.html +++ b/MovieVerse-Mobile/www/about.html @@ -6,6 +6,7 @@ +
diff --git a/MovieVerse-Mobile/www/app.js b/MovieVerse-Mobile/www/app.js index 3f9deeab..e7daefb6 100644 --- a/MovieVerse-Mobile/www/app.js +++ b/MovieVerse-Mobile/www/app.js @@ -1,13 +1,11 @@ const chatbotInput = document.getElementById("chatbotInput"); const chatbotBody = document.getElementById("chatbotBody"); +let initialMainContent; document.addEventListener('DOMContentLoaded', function() { - chatbotInput.addEventListener("keydown", function(event) { - if (event.key === "Enter") { - sendMessage(chatbotInput.value); - chatbotInput.value = ""; - } - }); + initialMainContent = document.getElementById('main').innerHTML; + initializeChatbot(); + document.getElementById('clear-search-btn').style.display = 'none'; }); function sendMessage(message) { @@ -17,15 +15,185 @@ function sendMessage(message) { let botReply = movieVerseResponse(message); // Renamed function for clarity setTimeout(() => { chatbotBody.innerHTML += ` -
Assistant: ${botReply}
+
MovieVerse Assistant: ${botReply}
`; }, 1000); } +const search = document.getElementById("search"); +const searchButton = document.getElementById("button-search"); +const searchTitle = document.getElementById("search-title"); +const otherTitle = document.getElementById("other1"); +const SEARCHPATH = "https://api.themoviedb.org/3/search/movie?&api_key=c5a20c861acf7bb8d9e987dcc7f1b558&query="; +const IMGPATH = "https://image.tmdb.org/t/p/w1280"; +const main = document.getElementById("main"); + +function calculateMoviesToDisplay() { + const screenWidth = window.innerWidth; + if (screenWidth <= 689.9) return 10; // 1 movie per row + if (screenWidth <= 1021.24) return 20; // 2 movies per row + if (screenWidth <= 1353.74) return 21; // 3 movies per row + if (screenWidth <= 1684.9) return 20; // 4 movies per row + if (screenWidth <= 2017.49) return 20; // 5 movies per row + if (screenWidth <= 2349.99) return 18; // 6 movies per row + if (screenWidth <= 2681.99) return 21; // 7 movies per row + if (screenWidth <= 3014.49) return 24; // 8 movies per row + if (screenWidth <= 3345.99) return 27; // 9 movies per row + if (screenWidth <= 3677.99) return 20; // 10 movies per row + if (screenWidth <= 4009.99) return 22; // 11 movies per row + if (screenWidth <= 4340.99) return 24; // 12 movies per row + if (screenWidth <= 4673.49) return 26; // 13 movies per row + if (screenWidth <= 5005.99) return 28; // 14 movies per row + if (screenWidth <= 5337.99) return 30; // 15 movies per row + if (screenWidth <= 5669.99) return 32; // 16 movies per row + if (screenWidth <= 6001.99) return 34; // 17 movies per row + if (screenWidth <= 6333.99) return 36; // 18 movies per row + if (screenWidth <= 6665.99) return 38; // 19 movies per row + if (screenWidth <= 6997.99) return 40; // 20 movies per row + if (screenWidth <= 7329.99) return 42; // 21 movies per row + if (screenWidth <= 7661.99) return 44; // 22 movies per row + if (screenWidth <= 7993.99) return 46; // 23 movies per row + if (screenWidth <= 8325.99) return 48; // 24 movies per row + return 20; +} + +function initializeChatbot() { + // Getting elements that may have been reloaded in the DOM + const chatbotInput = document.getElementById("chatbotInput"); + const chatbotBody = document.getElementById("chatbotBody"); + + // Reattaching the event listener for Enter key on chatbot input + chatbotInput.addEventListener("keydown", function(event) { + if (event.key === "Enter") { + sendMessage(chatbotInput.value); + chatbotInput.value = ""; + } + }); + + // Define the sendMessage function inside initializeChatbot to ensure scope + function sendMessage(message) { + chatbotBody.innerHTML += ` +
You: ${message}
+ `; + let botReply = movieVerseResponse(message); + setTimeout(() => { + chatbotBody.innerHTML += ` +
MovieVerse Assistant: ${botReply}
+ `; + }, 1000); + } +} + +function showMovies(movies, mainElement) { + mainElement.innerHTML = ''; + movies.forEach(movie => { + const { id, poster_path, title, vote_average, overview } = movie; + const movieEl = document.createElement('div'); + movieEl.classList.add('movie'); + const movieImage = poster_path + ? `${title}` + : `
Image Not Available
`; + + const voteAvg = vote_average.toFixed(1); + movieEl.innerHTML = ` + ${movieImage} +
+

${title}

+ ${voteAvg} +
+
+

Movie Intro:

+ ${overview} +
`; + + movieEl.addEventListener('click', () => { + localStorage.setItem('selectedMovieId', id); + window.location.href = 'movie-details.html'; + }); + + mainElement.appendChild(movieEl); + }); +} + +function getClassByRate(vote){ + if (vote >= 8) { + return 'green'; + } + else if (vote >= 5) { + return 'orange'; + } + else { + return 'red'; + } +} + +async function getMovies(url, mainElement) { + 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); + } + allMovies.sort((a, b) => b.vote_average - a.vote_average); + if (allMovies.length > 0) { + showMovies(allMovies.slice(0, numberOfMovies), mainElement); + document.getElementById('clear-search-btn').style.display = 'inline-block'; // Show the button + } + else { + mainElement.innerHTML = `

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

`; + document.getElementById('clear-search-btn').style.display = 'none'; // Hide the button if no results + } + document.getElementById('alt-title').innerHTML = ''; +} + +document.getElementById('clear-search-btn').addEventListener('click', function() { + document.getElementById('main').innerHTML = initialMainContent; + initializeChatbot(); // Re-initialize chatbot after restoring content + searchTitle.innerHTML = ''; + this.style.display = 'none'; +}); + +form.addEventListener('submit', (e) => { + e.preventDefault(); + const searchTerm = search.value.trim(); + if (searchTerm) { + getMovies(SEARCHPATH + searchTerm, main); + searchTitle.innerHTML = 'Search Results for: ' + searchTerm; + otherTitle.innerHTML = 'Check out other movies, too:'; + search.value = ''; + } + else { + searchTitle.innerHTML = 'Please enter a search term.'; + } +}); + +searchButton.addEventListener('click', (e) => { + e.preventDefault(); + const searchTerm = search.value; + if (searchTerm) { + getMovies(SEARCHPATH + searchTerm, main); + searchTitle.innerHTML = 'Search Results for: ' + searchTerm; + otherTitle.innerHTML = 'Check out other movies:'; + search.value = ''; + } + else { + searchTitle.innerHTML = 'Please enter a search term.'; + } +}); + +function clearMovieDetails() { + const movieDetailsContainer = document.getElementById('main'); + if (movieDetailsContainer) { + movieDetailsContainer.innerHTML = ''; + } +} + function movieVerseResponse(message) { const lowerMessage = message.toLowerCase(); - - if (lowerMessage.includes("hello") || lowerMessage.includes("hi")) { + if (lowerMessage.includes("hello") || lowerMessage.includes("hi") || lowerMessage.includes("hey")) { return "Hello! How can I assist you with MovieVerse today?"; } 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?"; @@ -143,8 +311,57 @@ function movieVerseResponse(message) { return "Of course! Let me know your favorite director, and I'll suggest a movie accordingly!"; } else if (lowerMessage.includes("movie suggestions based on year")) { return "Of course! Let me know your favorite year, and I'll suggest a movie accordingly!"; - } - else { + } 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")) { + return "Movies in the 1900s include: A Trip to the Moon, The Great Train Robbery, etc."; + } else if (lowerMessage.includes("1910s")) { + return "Movies in the 1910s include: The Birth of a Nation, Intolerance, etc."; + } else if (lowerMessage.includes("1920s")) { + return "Movies in the 1920s include: The Kid, The Gold Rush, etc."; + } 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")) { + return "Movies in the 1940s include: Citizen Kane, Casablanca, etc."; + } else if (lowerMessage.includes("1950s")) { + return "Movies in the 1950s include: Sunset Boulevard, Singin' in the Rain, etc."; + } else if (lowerMessage.includes("1960s")) { + return "Movies in the 1960s include: Psycho, The Apartment, etc."; + } else if (lowerMessage.includes("1970s")) { + return "Movies in the 1970s include: The Godfather, Star Wars, etc."; + } else if (lowerMessage.includes("1980s")) { + return "Movies in the 1980s include: Back to the Future, The Shining, etc."; + } else if (lowerMessage.includes("1990s")) { + return "Movies in the 1990s include: The Silence of the Lambs, Titanic, etc."; + } 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")) { + return "Movies in the 2010s include: Inception, The Avengers, etc."; + } else if (lowerMessage.includes("2020s")) { + return "Movies in the 2020s include: Tenet, Soul, etc."; + } 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")) { + return "Movies in 2023 include: The Flash, Black Panther: Wakanda Forever, etc."; + } else { return "Sorry, I didn't catch that. Can you rephrase or ask another question about movies?"; } } + +async function showMovieOfTheDay() { + const year = new Date().getFullYear(); + const url = `https://api.themoviedb.org/3/discover/movie?api_key=c5a20c861acf7bb8d9e987dcc7f1b558&sort_by=vote_average.desc&vote_count.gte=100&primary_release_year=${year}&vote_average.gte=7`; + try { + const response = await fetch(url); + const data = await response.json(); + const movies = data.results; + const randomMovie = movies[Math.floor(Math.random() * movies.length)]; + + localStorage.setItem('selectedMovieId', randomMovie.id); + window.location.href = 'movie-details.html'; + } + catch (error) { + console.error('Error fetching movie:', error); + alert('Failed to fetch the movie of the day. Please try again later.'); + } +} diff --git a/MovieVerse-Mobile/www/director-details.html b/MovieVerse-Mobile/www/director-details.html index bad693a0..ab067661 100644 --- a/MovieVerse-Mobile/www/director-details.html +++ b/MovieVerse-Mobile/www/director-details.html @@ -6,6 +6,7 @@ + diff --git a/MovieVerse-Mobile/www/discussions.css b/MovieVerse-Mobile/www/discussions.css index 10e34d4e..89878da7 100644 --- a/MovieVerse-Mobile/www/discussions.css +++ b/MovieVerse-Mobile/www/discussions.css @@ -14,6 +14,7 @@ body { background-size: cover; background-position: center; background-repeat: no-repeat; + background-attachment: fixed; overflow: hidden; height: 100%; } @@ -256,4 +257,38 @@ ol li:hover { position: fixed; top: 20px; right: -30px; -} \ No newline at end of file +} + +#movie-timeline-btn { + position: fixed; + bottom: 180px; + right: 30px; + background-color: #7378c5; + font: inherit; + color: black; + border-radius: 8px; + border: none; + cursor: pointer; +} + +#movie-timeline-btn:hover { + background-color: #ff8623; + transition: 0.3s ease-in; +} + +#movie-of-the-day-btn { + position: fixed; + bottom: 140px; + right: 30px; + background-color: #7378c5; + font: inherit; + color: black; + border-radius: 8px; + border: none; + cursor: pointer; +} + +#movie-of-the-day-btn:hover { + background-color: #ff8623; + transition: 0.1s linear; +} diff --git a/MovieVerse-Mobile/www/discussions.html b/MovieVerse-Mobile/www/discussions.html index 181c6f10..9db62698 100644 --- a/MovieVerse-Mobile/www/discussions.html +++ b/MovieVerse-Mobile/www/discussions.html @@ -11,26 +11,35 @@ - - + + + +
+
+

+ The MovieVerse +

+
+ - - -

Welcome to the MovieVerse Chatbot!

-
+ + +
+

The MovieVerse Assistant

+ +

Here, you can ask the chatbot about any movie-related topics or questions!

+
- -

The MovieVerse Assistant

-

Here, you can ask the chatbot about any movie-related topics or questions!

+
@@ -41,6 +50,10 @@

Here, you can ask the chatbot about any movie-related topics or questions! + + + + diff --git a/MovieVerse-Mobile/www/index.html b/MovieVerse-Mobile/www/index.html index d9c7f460..0d0212d3 100644 --- a/MovieVerse-Mobile/www/index.html +++ b/MovieVerse-Mobile/www/index.html @@ -285,6 +285,8 @@

Trending CLASSIC Movies:

+ + diff --git a/MovieVerse-Mobile/www/movie-details.html b/MovieVerse-Mobile/www/movie-details.html index 2249feda..b40ec7cc 100644 --- a/MovieVerse-Mobile/www/movie-details.html +++ b/MovieVerse-Mobile/www/movie-details.html @@ -5,6 +5,7 @@ Movie Details + diff --git a/MovieVerse-Mobile/www/movie-timeline.html b/MovieVerse-Mobile/www/movie-timeline.html new file mode 100644 index 00000000..f9afa9a7 --- /dev/null +++ b/MovieVerse-Mobile/www/movie-timeline.html @@ -0,0 +1,53 @@ + + + + + Movie Timeline + + + + + + +
+

+ The Movie Timeline +

+ + +
+ + +
+
+
+
+

Select a Period

+
+ + + +
+ +
+
+ +
+
+ + + + + + + + + + + + diff --git a/MovieVerse-Mobile/www/movie-timeline.js b/MovieVerse-Mobile/www/movie-timeline.js new file mode 100644 index 00000000..d206acbb --- /dev/null +++ b/MovieVerse-Mobile/www/movie-timeline.js @@ -0,0 +1,191 @@ +document.getElementById('load-movies').addEventListener('click', updateMovies); +document.getElementById('start-year').addEventListener('keypress', function(event) { + if (event.keyCode === 13) { + updateMovies(); + } +}); + +document.getElementById('end-year').addEventListener('keypress', function(event) { + if (event.keyCode === 13) { + updateMovies(); + } +}); + +const IMGPATH = "https://image.tmdb.org/t/p/w1280"; +const SEARCHPATH = "https://api.themoviedb.org/3/search/movie?&api_key=c5a20c861acf7bb8d9e987dcc7f1b558&query="; +const searchTitle = document.getElementById("select-text"); +const searchButton = document.getElementById("button-search"); +const search = document.getElementById("search"); +const main = document.getElementById("movies-container"); + +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); + } + allMovies.sort((a, b) => b.vote_average - a.vote_average); + if (allMovies.length > 0) { + showMovies(allMovies.slice(0, numberOfMovies), main); + } + else { + main.innerHTML = `

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

`; + } +} + +function clearMovieDetails() { + const movieDetailsContainer = document.getElementById('movie-details-container'); + if (movieDetailsContainer) { + movieDetailsContainer.innerHTML = ''; + } +} + +form.addEventListener('submit', (e) => { + e.preventDefault(); + const searchTerm = search.value.trim(); + if (searchTerm) { + getMovies(SEARCHPATH + searchTerm); + searchTitle.innerHTML = 'Search Results for: ' + searchTerm; + search.value = ''; + } + else { + searchTitle.innerHTML = 'Please enter a search term.'; + } + document.getElementById('clear-search-btn').style.display = 'block'; +}); + +searchButton.addEventListener('click', (e) => { + e.preventDefault(); + const searchTerm = search.value; + if (searchTerm) { + getMovies(SEARCHPATH + searchTerm); + searchTitle.innerHTML = 'Search Results for: ' + searchTerm; + search.value = ''; + } + else { + searchTitle.innerHTML = 'Please enter a search term.'; + } + document.getElementById('clear-search-btn').style.display = 'block'; +}); + +function updateMovies() { + let startYear = document.getElementById('start-year').value; + let endYear = document.getElementById('end-year').value; + let currentYear = new Date().getFullYear(); + if (startYear && endYear && startYear <= endYear && endYear <= currentYear) { + fetchMoviesByTimePeriod(startYear, endYear); + } + else { + alert('Please ensure the start year is before the end year, and both are not later than the current year.'); + } +} + +function showMovies(movies, mainElement) { + mainElement.innerHTML = ''; + movies.forEach(movie => { + const movieEl = document.createElement('div'); + movieEl.classList.add('movie'); + const movieImage = movie.poster_path + ? `${movie.title}` + : `
Image Not Available
`; + const voteAvg = movie.vote_average.toFixed(1); + const ratingClass = getClassByRate(movie.vote_average); + movieEl.innerHTML = ` + ${movieImage} +
+

${movie.title}

+ ${voteAvg} +
+
+

Movie Intro:

+ ${movie.overview} +
`; + movieEl.addEventListener('click', () => { + localStorage.setItem('selectedMovieId', movie.id); + window.location.href = 'movie-details.html'; + }); + movieEl.style.cursor = 'pointer'; + mainElement.appendChild(movieEl); + }); +} + +document.getElementById('clear-search-btn').addEventListener('click', () => { + location.reload(); +}); + +async function fetchMoviesByTimePeriod(startYear, endYear) { + const url = `https://api.themoviedb.org/3/discover/movie?api_key=c5a20c861acf7bb8d9e987dcc7f1b558&primary_release_date.gte=${startYear}-01-01&primary_release_date.lte=${endYear}-12-31`; + const response = await fetch(url); + const data = await response.json(); + const numberOfMovies = calculateMoviesToDisplay(); + const moviesToShow = data.results.slice(0, numberOfMovies); + showMovies(moviesToShow, document.getElementById('movies-container')); +} + +document.getElementById('load-movies').addEventListener('click', () => { + const startYear = document.getElementById('start-year').value; + const endYear = document.getElementById('end-year').value; + fetchMoviesByTimePeriod(startYear, endYear); +}); + +function calculateMoviesToDisplay() { + const screenWidth = window.innerWidth; + if (screenWidth <= 689.9) return 10; // 1 movie per row + if (screenWidth <= 1021.24) return 20; // 2 movies per row + if (screenWidth <= 1353.74) return 21; // 3 movies per row + if (screenWidth <= 1684.9) return 20; // 4 movies per row + if (screenWidth <= 2017.49) return 20; // 5 movies per row + if (screenWidth <= 2349.99) return 18; // 6 movies per row + if (screenWidth <= 2681.99) return 21; // 7 movies per row + if (screenWidth <= 3014.49) return 24; // 8 movies per row + if (screenWidth <= 3345.99) return 27; // 9 movies per row + if (screenWidth <= 3677.99) return 20; // 10 movies per row + if (screenWidth <= 4009.99) return 22; // 11 movies per row + if (screenWidth <= 4340.99) return 24; // 12 movies per row + if (screenWidth <= 4673.49) return 26; // 13 movies per row + if (screenWidth <= 5005.99) return 28; // 14 movies per row + if (screenWidth <= 5337.99) return 30; // 15 movies per row + if (screenWidth <= 5669.99) return 32; // 16 movies per row + if (screenWidth <= 6001.99) return 34; // 17 movies per row + if (screenWidth <= 6333.99) return 36; // 18 movies per row + if (screenWidth <= 6665.99) return 38; // 19 movies per row + if (screenWidth <= 6997.99) return 40; // 20 movies per row + if (screenWidth <= 7329.99) return 42; // 21 movies per row + if (screenWidth <= 7661.99) return 44; // 22 movies per row + if (screenWidth <= 7993.99) return 46; // 23 movies per row + if (screenWidth <= 8325.99) return 48; // 24 movies per row + return 20; +} + +function getClassByRate(vote) { + if (vote >= 8) { + return 'green'; + } + else if (vote >= 5) { + return 'orange'; + } + else { + return 'red'; + } +} + +async function showMovieOfTheDay(){ + const year = new Date().getFullYear(); + const url = `https://api.themoviedb.org/3/discover/movie?api_key=c5a20c861acf7bb8d9e987dcc7f1b558&sort_by=vote_average.desc&vote_count.gte=100&primary_release_year=${year}&vote_average.gte=7`; + try { + const response = await fetch(url); + const data = await response.json(); + const movies = data.results; + const randomMovie = movies[Math.floor(Math.random() * movies.length)]; + localStorage.setItem('selectedMovieId', randomMovie.id); + window.location.href = 'movie-details.html'; + } + catch (error) { + console.error('Error fetching movie:', error); + alert('Failed to fetch the movie of the day. Please try again later.'); + } +} diff --git a/MovieVerse-Mobile/www/style.css b/MovieVerse-Mobile/www/style.css index 4990350c..58c0ae17 100644 --- a/MovieVerse-Mobile/www/style.css +++ b/MovieVerse-Mobile/www/style.css @@ -313,6 +313,15 @@ h2:hover { transition: 0.1s linear; } +header { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 1.5rem; +} + + #movie-of-the-day-btn { position: fixed; bottom: 140px; @@ -923,3 +932,149 @@ p { .director-link:hover { color: #f509d9; } + +/* Movie Card Style */ +.movie-card { + background-color: #373b69; + border-radius: 3px; + box-shadow: 0 4px 5px rgba(0,0,0,0.2); + margin: 1rem; + color: white; + position: relative; + overflow: hidden; + width: 300px; +} + +.movie-card img { + width: 100%; +} + +.movie-info { + color: #eee; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.5rem 1rem 1rem; + letter-spacing: 0.5px; +} + +.movie-info h3 { + margin: 0; +} + +#load-movies { + padding: 10px 20px; + background-color: #7378c5; + color: white; + border: none; + border-radius: 5px; + cursor: pointer; + font-size: 1rem; +} + +#load-movies:hover { + background-color: #ff8623; +} + +.movie-info span { + background-color: #22254b; + border-radius: 3px; + font-weight: bold; + padding: 0.25rem 0.5rem; +} + +.timeline-container { + text-align: center; +} + +#load-movies { + cursor: pointer; + font: inherit; +} + +#movie-timeline-btn { + position: fixed; + bottom: 180px; + right: 30px; + background-color: #7378c5; + font: inherit; + color: black; + border-radius: 8px; + border: none; + cursor: pointer; +} + +#movie-timeline-btn:hover { + background-color: #ff8623; + transition: 0.3s ease-in; +} + +#movies-container { + display: flex; + flex-wrap: wrap; + justify-content: center; + align-items: stretch; + margin: 0 auto; + padding: 20px; +} + +.center-container { + text-align: center; + flex-direction: column; + align-items: center; + justify-content: center; + display: flex; +} + +#load-movies { + cursor: pointer; + font: inherit; +} + +#select-text { + margin-top: 60px; + color: #ff8623; +} + +#select-text:hover { + color: #f509d9; +} + +#search-title { + margin-top: 50px; +} + +#clear-search-btn { + background-color: #7378c5; + color: black; + border: none; + padding: 5px 10px; + border-radius: 5px; + cursor: pointer; + margin-left: 10px; + font: inherit; + margin-right: 20px; +} + +#clear-search-btn:hover { + background-color: #ff8623; +} + +@media screen and (-webkit-min-device-pixel-ratio: 0) { + body { + background-size: auto; + background-repeat: repeat; + } +} + +.year-input-container { + margin: 20px; +} + +.year-input { + width: 150px; + padding: 10px; + margin: 5px; + border-radius: 5px; + border: 1px solid #ddd; +} diff --git a/MovieVerse-Mobile/www/trivia.html b/MovieVerse-Mobile/www/trivia.html index fe315b30..ad68bf46 100644 --- a/MovieVerse-Mobile/www/trivia.html +++ b/MovieVerse-Mobile/www/trivia.html @@ -6,6 +6,7 @@ + diff --git a/about.html b/about.html index 012eec2c..2e34941c 100644 --- a/about.html +++ b/about.html @@ -16,7 +16,7 @@

The MovieVerse - About Us

- +
diff --git a/actor-details.html b/actor-details.html index 5a62cd68..31e09352 100644 --- a/actor-details.html +++ b/actor-details.html @@ -4,6 +4,7 @@ Actor Details + @@ -33,6 +34,7 @@

+
@@ -62,7 +64,7 @@

- + diff --git a/actor-details.js b/actor-details.js index 1cb7b08a..33a065e8 100644 --- a/actor-details.js +++ b/actor-details.js @@ -18,6 +18,10 @@ function getClassByRate(vote){ } } +document.getElementById('clear-search-btn').addEventListener('click', () => { + location.reload(); +}); + form.addEventListener('submit', (e) => { e.preventDefault(); const searchTerm = search.value.trim(); @@ -31,12 +35,12 @@ form.addEventListener('submit', (e) => { else { searchTitle.innerHTML = 'Please enter a search term.'; } + document.getElementById('clear-search-btn').style.display = 'block'; }); searchButton.addEventListener('click', (e) => { e.preventDefault(); const searchTerm = search.value; - if (searchTerm) { getMovies(SEARCHPATH + searchTerm); searchTitle.innerHTML = 'Search Results for: ' + searchTerm; @@ -46,6 +50,7 @@ searchButton.addEventListener('click', (e) => { else { searchTitle.innerHTML = 'Please enter a search term.'; } + document.getElementById('clear-search-btn').style.display = 'block'; }); function calculateMoviesToDisplay() { @@ -93,9 +98,11 @@ async function getMovies(url) { if (allMovies.length > 0) { showMovies(allMovies.slice(0, numberOfMovies)); + document.getElementById('clear-search-btn').style.display = 'block'; // Show the button } else { main.innerHTML = `

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

`; + document.getElementById('clear-search-btn').style.display = 'none'; // Hide the button if no results } } @@ -106,11 +113,9 @@ function showMovies(movies){ 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}
@@ -121,12 +126,10 @@ function showMovies(movies){

Movie Overview:

${overview}
`; - movieE1.addEventListener('click', () => { localStorage.setItem('selectedMovieId', id); // Store the movie ID window.location.href = 'movie-details.html'; }); - main.appendChild(movieE1); }); } @@ -138,7 +141,11 @@ function clearMovieDetails() { } } +let initialMainContent = ''; + document.addEventListener('DOMContentLoaded', () => { + initialMainContent = document.getElementById('main').innerHTML; + const actorId = localStorage.getItem('selectedActorId'); if (actorId) { fetchActorDetails(actorId); @@ -146,12 +153,15 @@ document.addEventListener('DOMContentLoaded', () => { else { document.getElementById('actor-details-container').innerHTML = '

Actor details not found.

'; } + + document.getElementById('clear-search-btn').style.display = 'none'; + + updateClock(); }); async function fetchActorDetails(actorId) { const actorUrl = `https://api.themoviedb.org/3/person/${actorId}?api_key=c5a20c861acf7bb8d9e987dcc7f1b558`; const creditsUrl = `https://api.themoviedb.org/3/person/${actorId}/movie_credits?api_key=c5a20c861acf7bb8d9e987dcc7f1b558`; - try { const [actorResponse, creditsResponse] = await Promise.all([ fetch(actorUrl), @@ -177,8 +187,6 @@ function populateActorDetails(actor, credits) { const actorImage = document.getElementById('actor-image'); const actorName = document.getElementById('actor-name'); const actorDescription = document.getElementById('actor-description'); - - // Check if actor image is available if (actor.profile_path) { actorImage.src = `https://image.tmdb.org/t/p/w1280${actor.profile_path}`; actorName.textContent = actor.name; @@ -194,7 +202,6 @@ function populateActorDetails(actor, credits) { } document.getElementById('actor-image').src = `https://image.tmdb.org/t/p/w1280${actor.profile_path}`; document.getElementById('actor-name').textContent = actor.name; - actorDescription.innerHTML = `

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

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

@@ -206,7 +213,6 @@ function populateActorDetails(actor, credits) { const filmographyHeading = document.createElement('p'); filmographyHeading.innerHTML = 'Filmography: '; document.getElementById('actor-description').appendChild(filmographyHeading); - const movieList = document.createElement('div'); movieList.classList.add('movie-list'); credits.cast.forEach(movie => { @@ -221,15 +227,11 @@ function populateActorDetails(actor, credits) { movieList.appendChild(document.createTextNode(', ')); }); filmographyHeading.appendChild(movieList); - - // Add Gender const gender = document.createElement('div'); gender.innerHTML = `

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

`; document.getElementById('actor-description').appendChild(gender); - - // Add Popularity Score const popularity = document.createElement('div'); popularity.innerHTML = `

Popularity Score: ${actor.popularity.toFixed(2)}

@@ -240,7 +242,6 @@ function populateActorDetails(actor, credits) { async function showMovieOfTheDay(){ const year = new Date().getFullYear(); const url = `https://api.themoviedb.org/3/discover/movie?api_key=c5a20c861acf7bb8d9e987dcc7f1b558&sort_by=vote_average.desc&vote_count.gte=100&primary_release_year=${year}&vote_average.gte=7`; - try { const response = await fetch(url); const data = await response.json(); @@ -267,6 +268,7 @@ function updateClock() { var now = new Date(); var hours = now.getHours(); var minutes = now.getMinutes(); + hours = hours < 10 ? '0' + hours : hours; minutes = minutes < 10 ? '0' + minutes : minutes; var timeString = hours + ':' + minutes; document.getElementById('clock').innerHTML = timeString; diff --git a/app.js b/app.js index 5e7681df..80ea3df2 100644 --- a/app.js +++ b/app.js @@ -1,13 +1,11 @@ const chatbotInput = document.getElementById("chatbotInput"); const chatbotBody = document.getElementById("chatbotBody"); +let initialMainContent; document.addEventListener('DOMContentLoaded', function() { - chatbotInput.addEventListener("keydown", function(event) { - if (event.key === "Enter") { - sendMessage(chatbotInput.value); - chatbotInput.value = ""; - } - }); + initialMainContent = document.getElementById('main').innerHTML; + initializeChatbot(); + document.getElementById('clear-search-btn').style.display = 'none'; }); function sendMessage(message) { @@ -17,15 +15,185 @@ function sendMessage(message) { let botReply = movieVerseResponse(message); // Renamed function for clarity setTimeout(() => { chatbotBody.innerHTML += ` -
Assistant: ${botReply}
+
MovieVerse Assistant: ${botReply}
`; }, 1000); } +const search = document.getElementById("search"); +const searchButton = document.getElementById("button-search"); +const searchTitle = document.getElementById("search-title"); +const otherTitle = document.getElementById("other1"); +const SEARCHPATH = "https://api.themoviedb.org/3/search/movie?&api_key=c5a20c861acf7bb8d9e987dcc7f1b558&query="; +const IMGPATH = "https://image.tmdb.org/t/p/w1280"; +const main = document.getElementById("main"); + +function calculateMoviesToDisplay() { + const screenWidth = window.innerWidth; + if (screenWidth <= 689.9) return 10; // 1 movie per row + if (screenWidth <= 1021.24) return 20; // 2 movies per row + if (screenWidth <= 1353.74) return 21; // 3 movies per row + if (screenWidth <= 1684.9) return 20; // 4 movies per row + if (screenWidth <= 2017.49) return 20; // 5 movies per row + if (screenWidth <= 2349.99) return 18; // 6 movies per row + if (screenWidth <= 2681.99) return 21; // 7 movies per row + if (screenWidth <= 3014.49) return 24; // 8 movies per row + if (screenWidth <= 3345.99) return 27; // 9 movies per row + if (screenWidth <= 3677.99) return 20; // 10 movies per row + if (screenWidth <= 4009.99) return 22; // 11 movies per row + if (screenWidth <= 4340.99) return 24; // 12 movies per row + if (screenWidth <= 4673.49) return 26; // 13 movies per row + if (screenWidth <= 5005.99) return 28; // 14 movies per row + if (screenWidth <= 5337.99) return 30; // 15 movies per row + if (screenWidth <= 5669.99) return 32; // 16 movies per row + if (screenWidth <= 6001.99) return 34; // 17 movies per row + if (screenWidth <= 6333.99) return 36; // 18 movies per row + if (screenWidth <= 6665.99) return 38; // 19 movies per row + if (screenWidth <= 6997.99) return 40; // 20 movies per row + if (screenWidth <= 7329.99) return 42; // 21 movies per row + if (screenWidth <= 7661.99) return 44; // 22 movies per row + if (screenWidth <= 7993.99) return 46; // 23 movies per row + if (screenWidth <= 8325.99) return 48; // 24 movies per row + return 20; +} + +function initializeChatbot() { + // Getting elements that may have been reloaded in the DOM + const chatbotInput = document.getElementById("chatbotInput"); + const chatbotBody = document.getElementById("chatbotBody"); + + // Reattaching the event listener for Enter key on chatbot input + chatbotInput.addEventListener("keydown", function(event) { + if (event.key === "Enter") { + sendMessage(chatbotInput.value); + chatbotInput.value = ""; + } + }); + + // Define the sendMessage function inside initializeChatbot to ensure scope + function sendMessage(message) { + chatbotBody.innerHTML += ` +
You: ${message}
+ `; + let botReply = movieVerseResponse(message); + setTimeout(() => { + chatbotBody.innerHTML += ` +
MovieVerse Assistant: ${botReply}
+ `; + }, 1000); + } +} + +function showMovies(movies, mainElement) { + mainElement.innerHTML = ''; + movies.forEach(movie => { + const { id, poster_path, title, vote_average, overview } = movie; + const movieEl = document.createElement('div'); + movieEl.classList.add('movie'); + const movieImage = poster_path + ? `${title}` + : `
Image Not Available
`; + + const voteAvg = vote_average.toFixed(1); + movieEl.innerHTML = ` + ${movieImage} +
+

${title}

+ ${voteAvg} +
+
+

Movie Intro:

+ ${overview} +
`; + + movieEl.addEventListener('click', () => { + localStorage.setItem('selectedMovieId', id); + window.location.href = 'movie-details.html'; + }); + + mainElement.appendChild(movieEl); + }); +} + +function getClassByRate(vote){ + if (vote >= 8) { + return 'green'; + } + else if (vote >= 5) { + return 'orange'; + } + else { + return 'red'; + } +} + +async function getMovies(url, mainElement) { + 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); + } + allMovies.sort((a, b) => b.vote_average - a.vote_average); + if (allMovies.length > 0) { + showMovies(allMovies.slice(0, numberOfMovies), mainElement); + document.getElementById('clear-search-btn').style.display = 'inline-block'; // Show the button + } + else { + mainElement.innerHTML = `

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

`; + document.getElementById('clear-search-btn').style.display = 'none'; // Hide the button if no results + } + document.getElementById('alt-title').innerHTML = ''; +} + +document.getElementById('clear-search-btn').addEventListener('click', function() { + document.getElementById('main').innerHTML = initialMainContent; + initializeChatbot(); // Re-initialize chatbot after restoring content + searchTitle.innerHTML = ''; + this.style.display = 'none'; +}); + +form.addEventListener('submit', (e) => { + e.preventDefault(); + const searchTerm = search.value.trim(); + if (searchTerm) { + getMovies(SEARCHPATH + searchTerm, main); + searchTitle.innerHTML = 'Search Results for: ' + searchTerm; + otherTitle.innerHTML = 'Check out other movies, too:'; + search.value = ''; + } + else { + searchTitle.innerHTML = 'Please enter a search term.'; + } +}); + +searchButton.addEventListener('click', (e) => { + e.preventDefault(); + const searchTerm = search.value; + if (searchTerm) { + getMovies(SEARCHPATH + searchTerm, main); + searchTitle.innerHTML = 'Search Results for: ' + searchTerm; + otherTitle.innerHTML = 'Check out other movies:'; + search.value = ''; + } + else { + searchTitle.innerHTML = 'Please enter a search term.'; + } +}); + +function clearMovieDetails() { + const movieDetailsContainer = document.getElementById('main'); + if (movieDetailsContainer) { + movieDetailsContainer.innerHTML = ''; + } +} + function movieVerseResponse(message) { const lowerMessage = message.toLowerCase(); - - if (lowerMessage.includes("hello") || lowerMessage.includes("hi")) { + if (lowerMessage.includes("hello") || lowerMessage.includes("hi") || lowerMessage.includes("hey")) { return "Hello! How can I assist you with MovieVerse today?"; } 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?"; @@ -143,8 +311,39 @@ function movieVerseResponse(message) { return "Of course! Let me know your favorite director, and I'll suggest a movie accordingly!"; } else if (lowerMessage.includes("movie suggestions based on year")) { return "Of course! Let me know your favorite year, and I'll suggest a movie accordingly!"; - } - else { + } 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")) { + return "Movies in the 1900s include: A Trip to the Moon, The Great Train Robbery, etc."; + } else if (lowerMessage.includes("1910s")) { + return "Movies in the 1910s include: The Birth of a Nation, Intolerance, etc."; + } else if (lowerMessage.includes("1920s")) { + return "Movies in the 1920s include: The Kid, The Gold Rush, etc."; + } 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")) { + return "Movies in the 1940s include: Citizen Kane, Casablanca, etc."; + } else if (lowerMessage.includes("1950s")) { + return "Movies in the 1950s include: Sunset Boulevard, Singin' in the Rain, etc."; + } else if (lowerMessage.includes("1960s")) { + return "Movies in the 1960s include: Psycho, The Apartment, etc."; + } else if (lowerMessage.includes("1970s")) { + return "Movies in the 1970s include: The Godfather, Star Wars, etc."; + } else if (lowerMessage.includes("1980s")) { + return "Movies in the 1980s include: Back to the Future, The Shining, etc."; + } else if (lowerMessage.includes("1990s")) { + return "Movies in the 1990s include: The Silence of the Lambs, Titanic, etc."; + } 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")) { + return "Movies in the 2010s include: Inception, The Avengers, etc."; + } else if (lowerMessage.includes("2020s")) { + return "Movies in the 2020s include: Tenet, Soul, etc."; + } 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")) { + return "Movies in 2023 include: The Flash, Black Panther: Wakanda Forever, etc."; + } else { return "Sorry, I didn't catch that. Can you rephrase or ask another question about movies?"; } } @@ -165,4 +364,17 @@ async function showMovieOfTheDay() { console.error('Error fetching movie:', error); alert('Failed to fetch the movie of the day. Please try again later.'); } -} \ No newline at end of file +} + +function updateClock() { + var now = new Date(); + var hours = now.getHours(); + var minutes = now.getMinutes(); + hours = hours < 10 ? '0' + hours : hours; + minutes = minutes < 10 ? '0' + minutes : minutes; + var timeString = hours + ':' + minutes; + document.getElementById('clock').innerHTML = timeString; +} + +setInterval(updateClock, 1000); +window.onload = updateClock; diff --git a/director-details.html b/director-details.html index b07ac982..f8fb231b 100644 --- a/director-details.html +++ b/director-details.html @@ -5,6 +5,7 @@ Director Details + @@ -33,6 +34,7 @@

+
@@ -62,7 +64,7 @@

- + diff --git a/director-details.js b/director-details.js index 4ba71a2b..3578b6ec 100644 --- a/director-details.js +++ b/director-details.js @@ -21,7 +21,6 @@ function getClassByRate(vote){ form.addEventListener('submit', (e) => { e.preventDefault(); const searchTerm = search.value.trim(); - if (searchTerm) { getMovies(SEARCHPATH + searchTerm); searchTitle.innerHTML = 'Search Results for: ' + searchTerm; @@ -31,12 +30,12 @@ form.addEventListener('submit', (e) => { else { searchTitle.innerHTML = 'Please enter a search term.'; } + document.getElementById('clear-search-btn').style.display = 'block'; }); searchButton.addEventListener('click', (e) => { e.preventDefault(); const searchTerm = search.value; - if (searchTerm) { getMovies(SEARCHPATH + searchTerm); searchTitle.innerHTML = 'Search Results for: ' + searchTerm; @@ -46,6 +45,7 @@ searchButton.addEventListener('click', (e) => { else { searchTitle.innerHTML = 'Please enter a search term.'; } + document.getElementById('clear-search-btn').style.display = 'block'; }); function calculateMoviesToDisplay() { @@ -89,18 +89,22 @@ async function getMovies(url) { allMovies = allMovies.concat(data.results); } - // Sort movies by vote_average in descending order allMovies.sort((a, b) => b.vote_average - a.vote_average); - // Display the sorted movies if (allMovies.length > 0) { showMovies(allMovies.slice(0, numberOfMovies)); + document.getElementById('clear-search-btn').style.display = 'block'; // Show the button } else { main.innerHTML = `

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

`; + document.getElementById('clear-search-btn').style.display = 'none'; // Hide the button if no results } } +document.getElementById('clear-search-btn').addEventListener('click', () => { + location.reload(); +}); + function showMovies(movies){ main.innerHTML = ''; movies.forEach((movie) => { @@ -108,11 +112,9 @@ function showMovies(movies){ 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}
@@ -123,12 +125,10 @@ function showMovies(movies){

Movie Overview:

${overview}
`; - movieE1.addEventListener('click', () => { localStorage.setItem('selectedMovieId', id); // Store the movie ID window.location.href = 'movie-details.html'; }); - main.appendChild(movieE1); }); } @@ -153,7 +153,6 @@ document.addEventListener('DOMContentLoaded', () => { async function fetchDirectorDetails(directorId) { const directorUrl = `https://api.themoviedb.org/3/person/${directorId}?api_key=c5a20c861acf7bb8d9e987dcc7f1b558`; const creditsUrl = `https://api.themoviedb.org/3/person/${directorId}/movie_credits?api_key=c5a20c861acf7bb8d9e987dcc7f1b558`; - try { const [directorResponse, creditsResponse] = await Promise.all([ fetch(directorUrl), @@ -180,7 +179,6 @@ function populateDirectorDetails(director, credits) { const directorImage = document.getElementById('director-image'); const directorName = document.getElementById('director-name'); const directorDescription = document.getElementById('director-description'); - if (director.profile_path) { directorImage.src = `https://image.tmdb.org/t/p/w1280${director.profile_path}`; directorName.textContent = director.name; @@ -194,7 +192,6 @@ function populateDirectorDetails(director, credits) { noImageText.style.textAlign = 'center'; document.querySelector('.director-left').appendChild(noImageText); } - directorDescription.innerHTML = `

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

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

@@ -205,7 +202,6 @@ function populateDirectorDetails(director, credits) { 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 => { @@ -235,6 +231,7 @@ function updateClock() { var now = new Date(); var hours = now.getHours(); var minutes = now.getMinutes(); + hours = hours < 10 ? '0' + hours : hours; minutes = minutes < 10 ? '0' + minutes : minutes; var timeString = hours + ':' + minutes; document.getElementById('clock').innerHTML = timeString; diff --git a/discussions.css b/discussions.css index f792b4fe..c1e5da30 100644 --- a/discussions.css +++ b/discussions.css @@ -10,12 +10,123 @@ body { margin: 0; align-content: center; text-align: center; + overflow: auto; + height: 100%; background-image: url("https://wallpaperaccess.com/full/433561.jpg"); background-size: cover; background-position: center; background-repeat: no-repeat; + background-attachment: fixed; + display: flex; + flex-direction: column; + align-items: center; + justify-content: flex-start; + min-height: 100vh; +} + +.overview { + position: absolute; + padding: 2rem; + top: 0; + left: 0; + bottom: 0; + right: 0; + background-color: #fff; + color: black; + transform: translateY(100%); + transition: opacity 0.3s ease-in, visibility 0.3s ease-in, transform 0.3s ease-in; + overflow-y: auto; + visibility: hidden; +} + +.movie:hover .overview { + transform: translateY(0); + opacity: 1; + visibility: visible; +} + +.overview h4 { + margin-top: 0; +} + +.movie-info span.green { + color: rgb(39,189,39); +} + +.movie-info span.orange { + color: orange; +} + +.movie-info span.red { + color: rgb(189,42,42); +} + +#my-heading { + color: #ff8623; + padding: 10px; + font-size: 36px; + text-align: center; + background-color: transparent; + margin-top: 50px; + margin-left: 35px; +} + +.movie { + background-color: #373b69; + border-radius: 3px; + box-shadow: 0 4px 5px rgba(0,0,0,0.2); + margin: 1rem; + color: white; + position: relative; overflow: hidden; - height: 100%; + width: 300px; +} + +.movie img { + width: 100%; +} + +.movie-info { + color: #eee; + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.5rem 1rem 1rem; + letter-spacing: 0.5px; +} + +.movie-info h3 { + margin: 0; +} + +.movie:hover .overview { + transform: translateY(0); +} + +.movie-info span { + background-color: #22254b; + border-radius: 3px; + font-weight: bold; + padding: 0.25rem 0.5rem; +} + +header { + display: flex; + align-items: center; + justify-content: space-between; + padding-left: 0.5vw; + padding-right: 3vw; + padding-top: -20px; + width: 96vw; +} + +.highlight { + color: orange; +} + +header h1 { + color: #ff8623; + text-align: center; } #movie-timeline-btn { @@ -28,6 +139,7 @@ body { border-radius: 8px; border: none; cursor: pointer; + z-index: 1000; } #movie-timeline-btn:hover { @@ -45,6 +157,7 @@ body { border-radius: 8px; border: none; cursor: pointer; + z-index: 1000; } #movie-of-the-day-btn:hover { @@ -166,16 +279,15 @@ ol li:hover { } #chatbotBody > div[text-align="right"] { - background-color: #ff8623; /* User messages background color */ - align-self: flex-end; /* Position the user messages to the right */ + background-color: #ff8623; + align-self: flex-end; } #chatbotBody > div[text-align="left"] { - background-color: #7378c5; /* Bot messages background color */ - align-self: flex-start; /* Position the bot messages to the left */ + background-color: #7378c5; + align-self: flex-start; } -/* Favorites Button */ .favorites-btn { background-color: #7378c5; border: none; @@ -189,8 +301,8 @@ ol li:hover { cursor: pointer; position: absolute; top: 0; - left: 289.5px; /* Center horizontally */ - transform: translateX(-50%); /* Adjust for the button's width */ + left: 288px; + transform: translateX(-50%); border-radius: 8px; padding-left: 10px; padding-right: 10px; @@ -202,7 +314,6 @@ ol li:hover { transition: 0.3s ease-in; } -/* About Button */ .about { background-color: #7378c5; border: none; @@ -218,7 +329,7 @@ ol li:hover { position: absolute; top: 0; left: 196px; - transform: translateX(-50%); /* Adjust for the button's width */ + transform: translateX(-50%); border-radius: 8px; } @@ -227,7 +338,6 @@ ol li:hover { transition: 0.3s ease-in; } -/* Back Button */ .back-btn { background-color: #7378c5; border: none; @@ -242,8 +352,8 @@ ol li:hover { cursor: pointer; position: absolute; top: 0; - left: 107px; /* Center horizontally */ - transform: translateX(-50%); /* Adjust for the button's width */ + left: 107px; + transform: translateX(-50%); border-radius: 8px; } @@ -279,6 +389,7 @@ ol li:hover { border-radius: 8px; border: none; cursor: pointer; + z-index: 1000; } #trivia-btn:hover { @@ -290,4 +401,137 @@ ol li:hover { position: fixed; top: 20px; right: -30px; +} + +@media (max-width: 906px) { + #local-time { + display: none; + } +} + +#local-time { + margin-top: 30px; + margin-left: 80px; + align-items: center; +} + +.clock:hover { + color: #f509d9; +} + +#time-label { + color: #ff8623; +} + +#time-label:hover { + color: #f509d9; +} + +.search { + background-color: transparent; + border: 2px solid #121280; + border-radius: 50px; + color: #fff; + font-family: inherit; + font-size: 1rem; + padding: 0.5rem 1rem; + width: 250px; +} + +main { + width: 100%; + display: flex; + justify-content: center; + align-items: stretch; + flex-wrap: wrap; + margin: 0 auto; + padding: 20px; +} + +#bot-header { + text-align: center; + width: 100%; +} + +.search::placeholder { + color: #7378c5; +} + +#search:hover { + outline: none; + background-color: #22254b; +} + +.search:focus { + outline: none; + background-color: #22254b; +} + +#button-search { + color: #171616; + background-color: #7378c5; + font: inherit; + border-radius: 8px; + border: none; + cursor: pointer; +} + +#button-search:hover { + background-color: #ff8623; + transition: 0.1s linear; +} + +#clear-search-btn { + background-color: #7378c5; + color: black; + border: none; + padding: 5px 10px; + border-radius: 5px; + cursor: pointer; + margin-left: 10px; + font: inherit; +} + +#clear-search-btn:hover { + background-color: #ff8623; +} + +@media (max-width: 906px) { + .back-btn, .about, .favorites-btn { + position: absolute; + top: 0; + left: 50%; + transform: translateX(-50%); + } + + .back-btn { + margin-left: -90px; + } + + .about { + margin-left: 0px; + } + + .favorites-btn { + margin-left: 93px; + } + + header { + padding-bottom: 20px; + justify-content: space-between; + padding: 1.5rem; + display: flex; + flex-direction: column; + align-items: center; + } + + main { + margin-bottom: -40px; + justify-content: space-between; + padding: 1.5rem; + display: flex; + flex-direction: column; + align-items: center; + } + } \ No newline at end of file diff --git a/discussions.html b/discussions.html index e1c82dc7..ae85c7be 100644 --- a/discussions.html +++ b/discussions.html @@ -11,27 +11,46 @@ - - + + + +
+
+

+ The MovieVerse +

+
+ +
+

Current Time:

+

+
+ - - - -

Welcome to the MovieVerse Chatbot!

+
+
+ + +
+
-
+ + +
+

The MovieVerse Assistant

+ +

Here, you can ask the chatbot about any movie-related topics or questions!

+
- -

The MovieVerse Assistant

-

Here, you can ask the chatbot about any movie-related topics or questions!

+
@@ -42,6 +61,10 @@

Here, you can ask the chatbot about any movie-related topics or questions! + + + + diff --git a/favorites.html b/favorites.html index 2303b610..dc69522f 100644 --- a/favorites.html +++ b/favorites.html @@ -5,6 +5,7 @@ Your Favorite Movies + @@ -29,7 +30,7 @@

- + diff --git a/movie-details.html b/movie-details.html index 6bf72173..06a439cd 100644 --- a/movie-details.html +++ b/movie-details.html @@ -5,6 +5,7 @@ Movie Details + @@ -32,6 +33,7 @@

+
@@ -69,7 +71,7 @@

- + diff --git a/movie-details.js b/movie-details.js index 2028b769..4ca27a49 100644 --- a/movie-details.js +++ b/movie-details.js @@ -6,6 +6,7 @@ const main = document.getElementById("main"); const IMGPATH = "https://image.tmdb.org/t/p/w1280"; const favoriteButton = document.getElementById("favorite-btn"); const searchTitle = document.getElementById("search-title"); +let initialMainContent; function getClassByRate(vote){ if (vote >= 8) { @@ -90,15 +91,15 @@ async function getMovies(url) { allMovies = allMovies.concat(data.results); } - // Sort movies by vote_average in descending order allMovies.sort((a, b) => b.vote_average - a.vote_average); - // Display the sorted movies if (allMovies.length > 0) { showMovies(allMovies.slice(0, numberOfMovies)); + document.getElementById('clear-search-btn').style.display = 'block'; // Show the button } else { main.innerHTML = `

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

`; + document.getElementById('clear-search-btn').style.display = 'none'; // Hide the button if no results } } @@ -142,6 +143,8 @@ function showMovies(movies){ } document.addEventListener('DOMContentLoaded', () => { + initialMainContent = document.getElementById('main').innerHTML; + const movieId = localStorage.getItem('selectedMovieId'); if (movieId) { fetchMovieDetails(movieId); @@ -149,6 +152,14 @@ document.addEventListener('DOMContentLoaded', () => { else { document.getElementById('movie-details-container').innerHTML = '

Movie details not found.

'; } + + document.getElementById('clear-search-btn').style.display = 'none'; + + updateClock(); +}); + +document.getElementById('clear-search-btn').addEventListener('click', () => { + location.reload(); }); async function fetchMovieDetails(movieId) { @@ -157,13 +168,11 @@ async function fetchMovieDetails(movieId) { const url2 = `https://api.themoviedb.org/3/movie/${movieId}?api_key=${code}&append_to_response=videos`; try { - // Fetch the movie details const response = await fetch(url); const movie = await response.json(); const response2 = await fetch(url2); const movie2 = await response2.json(); populateMovieDetails(movie); - // Fetch and display the trailer const trailers = movie2.videos.results.filter(video => video.type === 'Trailer'); if (trailers.length > 0) { const trailerUrl = `https://www.youtube.com/watch?v=${trailers[0].key}`; @@ -173,15 +182,18 @@ async function fetchMovieDetails(movieId) { catch (error) { console.error('Error fetching movie details:', error); } + const movieDetailsContainer = document.getElementById('movie-details-container'); + if (movieDetailsContainer.innerHTML.includes('Watch Trailer')) { + createTrailerButton(trailerUrl); // Use the appropriate trailer URL + } } function createTrailerButton(trailerUrl) { const trailerButton = document.createElement('button'); trailerButton.textContent = 'Watch Trailer'; trailerButton.onclick = () => window.open(trailerUrl, '_blank'); - trailerButton.classList.add('trailer-button'); // Add a class for styling + trailerButton.classList.add('trailer-button'); - // Find the element to insert the button after const movieRating = document.getElementById('movie-rating'); movieRating.parentNode.insertBefore(trailerButton, movieRating.nextSibling); } @@ -214,12 +226,10 @@ function populateMovieDetails(movie) { const movieRating = movie.vote_average.toFixed(1); document.getElementById('movie-image').src = `https://image.tmdb.org/t/p/w1280${movie.poster_path}`; document.getElementById('movie-title').textContent = movie.title; - // document.getElementById('movie-description').textContent = movie.overview; document.getElementById('movie-rating').textContent = `IMDB Rating: ${movieRating}`; document.title = movie.title + " - Movie Details"; const movieImage = document.getElementById('movie-image'); - const movieTitle = document.getElementById('movie-title'); const movieDescription = document.getElementById('movie-description'); if (movie.poster_path) { @@ -227,7 +237,6 @@ function populateMovieDetails(movie) { movieImage.alt = movie.title; } else { - // Hide the image element and show a message movieImage.style.display = 'none'; const noImageText = document.createElement('h2'); noImageText.textContent = 'Image Not Available'; @@ -290,17 +299,16 @@ function populateMovieDetails(movie) { } } - // Display the cast const castHeading = document.createElement('p'); castHeading.innerHTML = 'Cast: '; document.getElementById('movie-description').appendChild(castHeading); if (movie.credits && movie.credits.cast.length > 0) { - const topTenCast = movie.credits.cast.slice(0, 10); // Get only the first 10 actors + 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'); // Add class for styling + actorLink.classList.add('actor-link'); actorLink.addEventListener('click', () => { localStorage.setItem('selectedActorId', actor.id); window.location.href = 'actor-details.html'; @@ -308,7 +316,6 @@ function populateMovieDetails(movie) { castHeading.appendChild(actorLink); - // Add a comma after each actor name except the last one if (index < topTenCast.length - 1) { castHeading.appendChild(document.createTextNode(', ')); } @@ -335,14 +342,12 @@ function populateMovieDetails(movie) { movieLink.style.color = 'white'; }); movieLink.addEventListener('click', () => { - localStorage.setItem('selectedMovieId', similarMovie.id); // Store the clicked movie's ID - window.location.href = 'movie-details.html'; // Redirect to the details page + localStorage.setItem('selectedMovieId', similarMovie.id); + window.location.href = 'movie-details.html'; }); - // Append the clickable movie link similarMoviesHeading.appendChild(movieLink); - // If not the last movie, add a comma and space if (index < movie.similar.results.length - 1) { similarMoviesHeading.appendChild(document.createTextNode(', ')); } @@ -351,7 +356,6 @@ function populateMovieDetails(movie) { else { similarMoviesHeading.appendChild(document.createTextNode('None available.')); } - // Keywords const keywordsElement = document.createElement('p'); keywordsElement.innerHTML = `Keywords: ${keywords}`; movieDescription.appendChild(keywordsElement); @@ -369,7 +373,6 @@ async function showMovieOfTheDay(){ const movies = data.results; const randomMovie = movies[Math.floor(Math.random() * movies.length)]; - // Store the selected movie ID in localStorage and redirect to movie-details page localStorage.setItem('selectedMovieId', randomMovie.id); window.location.href = 'movie-details.html'; } @@ -383,6 +386,7 @@ function updateClock() { var now = new Date(); var hours = now.getHours(); var minutes = now.getMinutes(); + hours = hours < 10 ? '0' + hours : hours; minutes = minutes < 10 ? '0' + minutes : minutes; var timeString = hours + ':' + minutes; document.getElementById('clock').innerHTML = timeString; diff --git a/movie-timeline.html b/movie-timeline.html index 527a8db3..8872a6ec 100644 --- a/movie-timeline.html +++ b/movie-timeline.html @@ -5,6 +5,7 @@ Movie Timeline + @@ -37,6 +38,7 @@

Select a Period

+
@@ -49,7 +51,7 @@

Select a Period

- + diff --git a/movie-timeline.js b/movie-timeline.js index b5633f1f..0b21fb8b 100644 --- a/movie-timeline.js +++ b/movie-timeline.js @@ -23,17 +23,12 @@ async function getMovies(url) { 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); } - - // Sort movies by vote_average in descending order allMovies.sort((a, b) => b.vote_average - a.vote_average); - - // Display the sorted movies if (allMovies.length > 0) { showMovies(allMovies.slice(0, numberOfMovies), main); } @@ -52,7 +47,6 @@ function clearMovieDetails() { form.addEventListener('submit', (e) => { e.preventDefault(); const searchTerm = search.value.trim(); - if (searchTerm) { getMovies(SEARCHPATH + searchTerm); searchTitle.innerHTML = 'Search Results for: ' + searchTerm; @@ -61,12 +55,12 @@ form.addEventListener('submit', (e) => { else { searchTitle.innerHTML = 'Please enter a search term.'; } + document.getElementById('clear-search-btn').style.display = 'block'; }); searchButton.addEventListener('click', (e) => { e.preventDefault(); const searchTerm = search.value; - if (searchTerm) { getMovies(SEARCHPATH + searchTerm); searchTitle.innerHTML = 'Search Results for: ' + searchTerm; @@ -75,13 +69,13 @@ searchButton.addEventListener('click', (e) => { else { searchTitle.innerHTML = 'Please enter a search term.'; } + document.getElementById('clear-search-btn').style.display = 'block'; }); function updateMovies() { let startYear = document.getElementById('start-year').value; let endYear = document.getElementById('end-year').value; let currentYear = new Date().getFullYear(); - if (startYear && endYear && startYear <= endYear && endYear <= currentYear) { fetchMoviesByTimePeriod(startYear, endYear); } @@ -100,7 +94,6 @@ function showMovies(movies, mainElement) { : `
Image Not Available
`; const voteAvg = movie.vote_average.toFixed(1); const ratingClass = getClassByRate(movie.vote_average); - movieEl.innerHTML = ` ${movieImage}
@@ -111,19 +104,19 @@ function showMovies(movies, mainElement) {

Movie Intro:

${movie.overview}
`; - movieEl.addEventListener('click', () => { localStorage.setItem('selectedMovieId', movie.id); window.location.href = 'movie-details.html'; }); - movieEl.style.cursor = 'pointer'; - mainElement.appendChild(movieEl); }); } -// Function to fetch movies based on the selected time period +document.getElementById('clear-search-btn').addEventListener('click', () => { + location.reload(); +}); + async function fetchMoviesByTimePeriod(startYear, endYear) { const url = `https://api.themoviedb.org/3/discover/movie?api_key=c5a20c861acf7bb8d9e987dcc7f1b558&primary_release_date.gte=${startYear}-01-01&primary_release_date.lte=${endYear}-12-31`; const response = await fetch(url); @@ -133,7 +126,6 @@ async function fetchMoviesByTimePeriod(startYear, endYear) { showMovies(moviesToShow, document.getElementById('movies-container')); } -// Event listener for the time period selection (to be adapted based on your HTML) document.getElementById('load-movies').addEventListener('click', () => { const startYear = document.getElementById('start-year').value; const endYear = document.getElementById('end-year').value; @@ -185,6 +177,7 @@ function updateClock() { var now = new Date(); var hours = now.getHours(); var minutes = now.getMinutes(); + hours = hours < 10 ? '0' + hours : hours; minutes = minutes < 10 ? '0' + minutes : minutes; var timeString = hours + ':' + minutes; document.getElementById('clock').innerHTML = timeString; @@ -196,14 +189,11 @@ window.onload = updateClock; async function showMovieOfTheDay(){ const year = new Date().getFullYear(); const url = `https://api.themoviedb.org/3/discover/movie?api_key=c5a20c861acf7bb8d9e987dcc7f1b558&sort_by=vote_average.desc&vote_count.gte=100&primary_release_year=${year}&vote_average.gte=7`; - try { const response = await fetch(url); const data = await response.json(); const movies = data.results; const randomMovie = movies[Math.floor(Math.random() * movies.length)]; - - // Store the selected movie ID in localStorage and redirect to movie-details page localStorage.setItem('selectedMovieId', randomMovie.id); window.location.href = 'movie-details.html'; } diff --git a/quiz.js b/quiz.js index 5d7aba67..2a007a91 100644 --- a/quiz.js +++ b/quiz.js @@ -84,12 +84,12 @@ form.addEventListener('submit', (e) => { if (searchTerm) { getMovies(SEARCHPATH + searchTerm); searchTitle.innerHTML = 'Search Results for: ' + searchTerm; - otherTitle.innerHTML = 'Check out other movies:'; search.value = ''; } else { searchTitle.innerHTML = 'Please enter a search term.'; } + document.getElementById('clear-search-btn').style.display = 'block'; // Show the button }); searchButton.addEventListener('click', (e) => { @@ -99,12 +99,12 @@ searchButton.addEventListener('click', (e) => { if (searchTerm) { getMovies(SEARCHPATH + searchTerm); searchTitle.innerHTML = 'Search Results for: ' + searchTerm; - otherTitle.innerHTML = 'Check out other movies:'; search.value = ''; } else { searchTitle.innerHTML = 'Please enter a search term.'; } + document.getElementById('clear-search-btn').style.display = 'block'; // Show the button }); async function getMovies(url) { @@ -121,21 +121,30 @@ async function getMovies(url) { // Sort movies by vote_average in descending order allMovies.sort((a, b) => b.vote_average - a.vote_average); + document.getElementById('clear-search-btn').style.display = 'block'; // Display the sorted movies if (allMovies.length > 0) { showMovies(allMovies.slice(0, numberOfMovies)); + document.getElementById('clear-search-btn').style.display = 'none'; // Hide the button if no results } else { main.innerHTML = `

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

`; + document.getElementById('clear-search-btn').style.display = 'none'; // Hide the button if no results } } +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){ @@ -199,6 +208,10 @@ function calculateMoviesToDisplay() { 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()); diff --git a/script.js b/script.js index c0536233..f258f3d4 100644 --- a/script.js +++ b/script.js @@ -427,6 +427,7 @@ function updateClock() { var now = new Date(); var hours = now.getHours(); var minutes = now.getMinutes(); + hours = hours < 10 ? '0' + hours : hours; minutes = minutes < 10 ? '0' + minutes : minutes; var timeString = hours + ':' + minutes; document.getElementById('clock').innerHTML = timeString; diff --git a/style.css b/style.css index 4c73c198..bb0cf002 100644 --- a/style.css +++ b/style.css @@ -205,11 +205,15 @@ header h1 { background-color: #fff; color: black; transform: translateY(100%); - transition: 0.3s ease-in; + transition: opacity 0.3s ease-in, visibility 0.3s ease-in, transform 0.3s ease-in; + overflow-y: auto; + visibility: hidden; } .movie:hover .overview { transform: translateY(0); + opacity: 1; + visibility: visible; } .overview h4 { @@ -1095,7 +1099,6 @@ p { } } - .year-input-container { margin: 20px; } @@ -1122,7 +1125,6 @@ p { background-color: #ff8623; } -/* Movie Card Style */ .movie-card { background-color: #373b69; border-radius: 3px; @@ -1158,6 +1160,10 @@ p { padding: 0.25rem 0.5rem; } +.timeline-container { + text-align: center; +} + #movie-timeline-btn { position: fixed; bottom: 180px; @@ -1189,6 +1195,7 @@ p { flex-direction: column; align-items: center; justify-content: center; + display: flex; } #load-movies { @@ -1203,4 +1210,27 @@ p { #select-text:hover { color: #f509d9; +} + +#clear-search-btn { + background-color: #7378c5; + color: black; + border: none; + padding: 5px 10px; + border-radius: 5px; + cursor: pointer; + margin-left: 10px; + font: inherit; + margin-right: 20px; +} + +#clear-search-btn:hover { + background-color: #ff8623; +} + +@media screen and (-webkit-min-device-pixel-ratio: 0) { + body { + background-size: auto; + background-repeat: repeat; + } } \ No newline at end of file diff --git a/trivia.css b/trivia.css index 9af0f26a..b889e01c 100644 --- a/trivia.css +++ b/trivia.css @@ -311,11 +311,15 @@ header h1 { background-color: #fff; color: black; transform: translateY(100%); - transition: 0.3s ease-in; + transition: opacity 0.3s ease-in, visibility 0.3s ease-in, transform 0.3s ease-in; + overflow-y: auto; + visibility: hidden; } .movie:hover .overview { transform: translateY(0); + opacity: 1; + visibility: visible; } .overview h4 { @@ -434,3 +438,105 @@ main { #trivia-label:hover { color: #f509d9; } + +#clear-search-btn { + background-color: #7378c5; + color: black; + border: none; + padding: 5px 10px; + border-radius: 5px; + cursor: pointer; + margin-left: 10px; + font: inherit; + margin-right: 20px; +} + +#clear-search-btn:hover { + background-color: #ff8623; +} + +@media (max-width: 900px) { + #local-time { + display: none; + } +} + +@media (max-width: 840px) { + .back-btn { + background-color: #7378c5; + border: none; + color: white; + width: 85px; + height: 40px; + text-align: center; + text-decoration: none; + display: inline-block; + font: inherit; + font-size: 16px; + cursor: pointer; + position: absolute; + top: 0; + margin-left: -200px; + border-radius: 8px; + } + + .about { + background-color: #7378c5; + border: none; + color: white; + width: 85px; + height: 40px; + text-align: center; + text-decoration: none; + display: inline-block; + font: inherit; + font-size: 16px; + cursor: pointer; + position: absolute; + top: 0; + margin-left: -20px; + border-radius: 8px; + } + + .favorites-btn { + background-color: #7378c5; + border: none; + color: white; + height: 40px; + text-align: center; + text-decoration: none; + display: inline-block; + font: inherit; + font-size: 16px; + cursor: pointer; + position: absolute; + top: 0; + margin-left: 168px; + border-radius: 8px; + padding-left: 10px; + padding-right: 10px; + box-sizing: border-box; + } + + header { + padding-bottom: 20px; + justify-content: space-between; + padding: 1.5rem; + display: flex; + flex-direction: column; + align-items: center; + } + + main { + margin-bottom: -40px; + justify-content: space-between; + padding: 1.5rem; + display: flex; + flex-direction: column; + align-items: center; + } + + #trivia-label { + margin-top: 60px; + } +} diff --git a/trivia.html b/trivia.html index a048fc8a..380c55ab 100644 --- a/trivia.html +++ b/trivia.html @@ -2,9 +2,10 @@ - Movies Trivia Quiz + MovieVerse Trivia + @@ -28,7 +29,10 @@

-

Movie Trivia Quiz

+
+

Movie Trivia Quiz

+ +