diff --git a/README.md b/README.md
index 49800f55..39b6a5f3 100644
--- a/README.md
+++ b/README.md
@@ -4,10 +4,17 @@
Project of animes and mangas website, utilizing the AniList, Consumet and Aniwatch API, which has info of animes and mangas released, data of the cast of that media, and many other things.
-
You can access this website on:
+
+AniProject on Vercel
+or
+AniProject on Render
+If the Vercel one is blocked by payment, try this one (it takes up to 2 minutes to deploy and load due to cold boot).
+
+
> [!CAUTION]
-> Please note that this project is strictly non-commercial. It is not permitted to generate revenue or include advertisements using this project. Violating this policy may result in legal actions by the respective owners of these intellectual properties.
+> Please note that this project is strictly non-commercial. It is not permitted to generate revenue or include advertisements using this project. Violating this policy may result in legal actions by the respective owners of these intellectual properties.
## Navigation
@@ -20,7 +27,7 @@ Project of animes and mangas website, utilizing the AniList, Consumet and Aniwat
- [Authentication](#authentication)
- [Collections and Documents](#collections-and-documents)
- [Users](#users)
- - [Comments](#comments)
+ - [Comments](#[DEPRECATED]comments)
- [Notifications](#notifications)
- [Previews/Screenshots](#camera-previewscreenshots)
@@ -29,7 +36,6 @@ Project of animes and mangas website, utilizing the AniList, Consumet and Aniwat
- [x] `Search`: Get a list of all animes and mangas you want using filters.
- [x] `Watch`: Watch any available episode Dubbed or Subbed.
- [x] `Read`: Read any manga chapter available.
-- [x] `Comment`: Write what you thougth of that episode or just tell something that every should know about.
- [x] `Log In`: You can log in with Google, Anilist or Anonymously (with some restrictions).
- [x] `Anilist Integration`: Use your Anilist account, carry over your settings, animes and mangas.
- [x] `Keep Watching`: Continue the episode from where you stop last time.
@@ -37,11 +43,12 @@ Project of animes and mangas website, utilizing the AniList, Consumet and Aniwat
- [x] `Mark your favourite animes e mangas`: Save them as Completed, Dropped, Planning, and more.
- [x] `Mark the episodes you watched`: And keep watching from there later
- [x] `News Feed`: Keep up with the latest news of animes, mangas and the industry.
+- [DEPRECATED] `Comment`: Write what you thought of that episode or just tell something that every should know about.
## :rocket: Quick Deploy
> [!IMPORTANT]
-> You NEED to make some configurations to use the project properly. Give a look on the [How Can i Run It Section](#computer-how-can-i-run-it) then use the info you got there on Vercel or any other platform `Enviroment Variables Section`.
+> You NEED to make some configurations to use the project properly. Give a look on the [How Can i Run It Section](#computer-how-can-i-run-it) then use the info you got there on Vercel or any other platform `Enviroment Variables Section`.
On Vercel: [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2FErickLimaS%2Fanime-website&env=NEXT_PUBLIC_ANIWATCH_API_URL,NEXT_PUBLIC_CONSUMET_API_URL&envDescription=First%2C%20click%20on%20%22Learn%20More%22%20to%20ensure%20all%20variables%20is%20correct%20and%20setted.%20To%20watch%2C%20you'll%20need%20to%20deploy%20the%20Consumet%20and%20Aniwatch%20API.%20To%20the%20login%20system%2C%20you'll%20need%20to%20setup%20a%20Firebase%20Project%2C%20and%20or%20set%20the%20Dev%20Mode%20on%20Anilist%20to%20use%20their%20login.%20&envLink=https%3A%2F%2Fgithub.com%2FErickLimaS%2Fanime-website%3Ftab%3Dreadme-ov-file%23computer-how-can-i-run-it)
@@ -49,7 +56,6 @@ On Vercel: [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com
- [ ] `Bug Fixes`
- [ ] `Hide Next Episode Images Until You Watch It (avoid spoilers)`
-- [ ] `AniList Reviews/Comments`
- [ ] `AniList Threads`
- [ ] `New Media Sources`
- [ ] `New Video Player Features`
@@ -95,64 +101,68 @@ npm install
3. Now you will need to create a `.env.local` file or fill the `.env.example` on the `project root folder`, and follow the instructions bellow.
- - **External APIs** (go to these repos, host your own instance and save the URL to use on ``.env.local``):
+ - **External APIs** (go to these repos, host your own instance and save the URL to use on `.env.local`):
- Consumet API
- Aniwatch API
-
- **Anilist Login** (OAuth):
- You need to first login on your account on Anilist.
- Then go to Developer Page on the Settings and click "Create New Client".
- Now you need to add the name of your forked project/website and the URL to redirect when user accept the login, then hit "Save".
- Store the Client ID and Secret on your ".env.local".
- TIP: Create 2 of these, one for the dev env and other to production.
-
- **Firebase** (to use Google, Email and Anonymous Login and the Firestore Database):
+
- Create a project for this fork/clone you did on Firebase.
- All the Firebase info needed on `.env.local` **can be found when you create a new project**.
- **IMPORTANT**: Make Sure to ALLOW your Hosted Website Domain on Firebase Authentication!
- **IMPORTANT**: You'll need to **change the Rules** on **Firestore Database**. There is 2 options depending of what login methods you will use:
+
- With **ALL** Login Methods available:
+
```javascript
rules_version = '2';
-
+
service cloud.firestore {
match /databases/{database}/documents {
-
+
match /{document=**} {
// will allow any write and read operation. No conditions due to Anilist OAuth Login.
allow read, write: if true;
}
-
+
}
}
```
+
- With **ONLY** Firebase Login Methods (no Anilist Login):
+
```javascript
rules_version = '2';
-
+
service cloud.firestore {
match /databases/{database}/documents {
-
+
match /users/{document=**} {
// allows only requests if a userUID is available.
allow read, write: request.auth.uid != null;
}
-
+
match /comments/{document=**} {
- // allows only write request if a userUID is available.
+ // allows only write request if a userUID is available.
allow read: if true;
allow write: request.auth.uid != null;
}
-
+
match /notifications/{document=**} {
- // allows only write request if a userUID is available.
+ // allows only write request if a userUID is available.
allow read: if true;
allow write: request.auth.uid != null;
}
-
+
}
}
```
+
- OPTIONAL: This project uses a JSON file (47 mb) filled with Animes and Mangas data as a offline Database. This repository already has this file, but it might be outdated, so you decide if you want to ignore this step.
- Go to anime-offline-database and download the JSON file that will be used on only `Search Page` (or you can make some changes and use some API to fetch the data).
- With the file downloaded, put it in the `/app/api/animes-database` directory, replacing the previous one.
@@ -162,15 +172,20 @@ With all that done, you can follow the pre-made `.env.example` on the root folde
```javascript
// Consumet API
NEXT_PUBLIC_CONSUMET_API_URL=https://your-hosted-consumet-api-url.com
+
// Aniwatch API
NEXT_PUBLIC_ANIWATCH_API_URL=https://your-hosted-aniwatch-api-url.com
+
// Anilist OAuth Settings
NEXT_PUBLIC_ANILIST_CLIENT_ID=your-anilist-client-id
ANILIST_CLIENT_SECRET=your-anilist-secret
+
// Next.js Route Handler - Make sure to add the pathname "/api/animes-database" bellow
NEXT_PUBLIC_NEXT_ROUTE_HANDLER_API=https://url-to-where-your-website-is-hosted.com/api/animes-database
+
// Bellow is the url to use ONLY on Dev Enviroment. You WILL NEED TO CHANGE IT when on hosted mode to the respective url. Look for something like Enviroment Variables to do it.
NEXT_PUBLIC_WEBSITE_ORIGIN_URL=http://localhost:3000
+
// Firebase Settings
NEXT_PUBLIC_FIREBASE_API_KEY=firebase-setting-related-to-this-field
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN=firebase-setting-related-to-this-field
@@ -180,6 +195,9 @@ NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER=firebase-setting-related-to-this-field
NEXT_PUBLIC_FIREBASE_APP_ID=firebase-setting-related-to-this-field
NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID=firebase-setting-related-to-this-field
NEXT_PUBLIC_FIREBASE_DATABASE_URL=firebase-setting-related-to-this-field
+
+// GOOGLE ANALYTICS: optional
+NEXT_PUBLIC_MEASUREMENT_ID=your-measurement-id
```
4. Now run `npm run dev` to initialize the website
@@ -207,7 +225,7 @@ It is used to store on User Document things like:
- User Profile Photo
- Username
- Preferences (media source, adult content, subtitles and more)
-- Comments
+- [DEPRECATED]Comments
- Notifications
- Bookmarked Medias
- Currently Watching Medias
@@ -222,7 +240,7 @@ With Firebase Database, we have 3 Collections:
Stores only Users Documents after a successfull signup.
-#### Comments
+#### [DEPRECATED]Comments
Stores comments made on episodes or on its main page.
@@ -287,7 +305,7 @@ In this document, has info of all episodes already notified to any user and the
![News Article Page 1](https://github.com/ErickLimaS/anime-website/assets/69987890/de58a15a-eed4-4cdc-9249-900a90c21c50)
-## Inspiration
+## Inspiration
-- Template
-- Media Page
\ No newline at end of file
+- Template
+- Media Page
diff --git a/app/api/anilist/anilistQueryConstants.ts b/app/api/anilist/anilistQueryConstants.ts
index a352ec7a..226386c1 100644
--- a/app/api/anilist/anilistQueryConstants.ts
+++ b/app/api/anilist/anilistQueryConstants.ts
@@ -2,6 +2,7 @@ import {
queryCharacters,
queryCoverImage,
queryMediaListEntry,
+ queryMediaReviews,
queryMediaTags,
queryNextAiringEpisode,
queryRecommendationsByCurrMedia,
@@ -43,6 +44,7 @@ export function requestMedias(
isFavourite
${queryMediaListEntry}
status
+ ${queryMediaReviews}
${queryRelatedMediasBasicInfo}
episodes
chapters
@@ -105,6 +107,7 @@ export function requestMediaById(isUserAuthenticated: boolean) {
isAdult
status
isFavourite
+ ${queryMediaReviews}
${queryMediaListEntry}
${queryRelatedMediasBasicInfo}
episodes
@@ -183,6 +186,7 @@ export function requestMediasByDateAndTimeRelease() {
idMal
isAdult
description
+ ${queryMediaReviews}
${queryTitles}
id
${queryCoverImage}
@@ -254,6 +258,7 @@ export function mediaTrendingApiQueryRequest() {
isAdult
${queryTitles}
id
+ ${queryMediaReviews}
${queryCoverImage}
trailer{
id
diff --git a/app/api/anilist/queryModulesByCategory.ts b/app/api/anilist/queryModulesByCategory.ts
index c4d20fbe..7adaf073 100644
--- a/app/api/anilist/queryModulesByCategory.ts
+++ b/app/api/anilist/queryModulesByCategory.ts
@@ -314,3 +314,26 @@ export const queryRecommendationsByCurrMediaUserAuthenticated = `recommendations
}
}
}`;
+
+export const queryMediaReviews = `reviews {
+ nodes {
+ id
+ summary
+ body(asHtml: true)
+ rating
+ userRating
+ ratingAmount
+ score
+ user {
+ id
+ name
+ about
+ avatar {
+ large
+ medium
+ }
+ bannerImage
+ }
+ }
+ }
+ `;
diff --git a/app/components/CommentsSection/component.module.css b/app/components/[DEPRECATED]CommentsSection/component.module.css
similarity index 100%
rename from app/components/CommentsSection/component.module.css
rename to app/components/[DEPRECATED]CommentsSection/component.module.css
diff --git a/app/components/CommentsSection/components/CommentContainer/component.module.css b/app/components/[DEPRECATED]CommentsSection/components/CommentContainer/component.module.css
similarity index 100%
rename from app/components/CommentsSection/components/CommentContainer/component.module.css
rename to app/components/[DEPRECATED]CommentsSection/components/CommentContainer/component.module.css
diff --git a/app/components/CommentsSection/components/CommentContainer/index.tsx b/app/components/[DEPRECATED]CommentsSection/components/CommentContainer/index.tsx
similarity index 100%
rename from app/components/CommentsSection/components/CommentContainer/index.tsx
rename to app/components/[DEPRECATED]CommentsSection/components/CommentContainer/index.tsx
diff --git a/app/components/CommentsSection/components/WriteCommentForm/component.module.css b/app/components/[DEPRECATED]CommentsSection/components/WriteCommentForm/component.module.css
similarity index 100%
rename from app/components/CommentsSection/components/WriteCommentForm/component.module.css
rename to app/components/[DEPRECATED]CommentsSection/components/WriteCommentForm/component.module.css
diff --git a/app/components/CommentsSection/components/WriteCommentForm/index.tsx b/app/components/[DEPRECATED]CommentsSection/components/WriteCommentForm/index.tsx
similarity index 100%
rename from app/components/CommentsSection/components/WriteCommentForm/index.tsx
rename to app/components/[DEPRECATED]CommentsSection/components/WriteCommentForm/index.tsx
diff --git a/app/components/CommentsSection/index.tsx b/app/components/[DEPRECATED]CommentsSection/index.tsx
similarity index 100%
rename from app/components/CommentsSection/index.tsx
rename to app/components/[DEPRECATED]CommentsSection/index.tsx
diff --git a/app/media/[id]/components/Reviews/component.module.css b/app/media/[id]/components/Reviews/component.module.css
new file mode 100644
index 00000000..1d000859
--- /dev/null
+++ b/app/media/[id]/components/Reviews/component.module.css
@@ -0,0 +1,145 @@
+h2.heading_style {
+
+ font-family: var(--font-family-sans-serif);
+
+ margin: 8px 0;
+
+ color: var(--white-50);
+
+ font-size: var(--font-size--h5);
+}
+
+
+#reviews_container>ul {
+
+ display: flex;
+ flex-direction: column;
+
+ gap: 24px 0;
+
+}
+
+.review_heading {
+
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+
+ gap: 0 24px;
+
+ margin: 16px 0;
+
+ font-family: var(--font-family-sans-serif);
+
+}
+
+.review_heading img {
+
+ border-radius: 50%;
+
+}
+
+.review_heading h3 {
+
+ color: var(--white-100);
+ font-size: var(--font-size--h5);
+
+}
+
+.review_body small {
+
+ color: var(--white-75);
+
+ display: block;
+ margin: 24px 0;
+
+}
+
+.review_text_container {
+
+ font-family: var(--font-family-sans-serif);
+
+ position: relative;
+ display: flex;
+
+}
+
+.review_text_container .quote_svg_container {
+
+ position: absolute;
+ top: 0;
+ left: 0;
+
+}
+
+@media(min-width:840px) {
+
+ .review_text_container .quote_svg_container {
+
+ left: -24px;
+
+ }
+
+}
+
+.review_text_container .quote_svg_container>svg {
+
+ transform: scale(1.8);
+ fill: var(--brand-color);
+
+}
+
+.review_text_container>div {
+
+ position: relative;
+
+ top: 0;
+ left: 24px;
+
+ width: calc(100% - 24px);
+
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 5;
+ overflow: hidden;
+
+}
+
+@media(min-width:840px) {
+ .review_text_container>div {
+
+ width: 100%;
+
+ left: 0;
+ }
+}
+
+.review_text_container>div>* {
+
+ margin-bottom: 8px;
+
+}
+
+.review_text_container * {
+ color: var(--white-100);
+}
+
+.review_text_container img {
+ width: 95%;
+}
+
+.review_container>a {
+
+ color: var(--brand-color);
+ width: fit-content;
+
+ display: flex;
+ gap: 0 16px;
+
+ margin: 24px 0;
+ padding: 8px;
+
+ border: 1px solid var(--brand-color);
+ border-radius: 4px;
+
+}
\ No newline at end of file
diff --git a/app/media/[id]/components/Reviews/components/AnchorTag/component.module.css b/app/media/[id]/components/Reviews/components/AnchorTag/component.module.css
new file mode 100644
index 00000000..4ddd42ac
--- /dev/null
+++ b/app/media/[id]/components/Reviews/components/AnchorTag/component.module.css
@@ -0,0 +1,17 @@
+.anchor_tag {
+
+ font-family: var(--font-family-sans-serif);
+ color: var(--brand-color);
+ width: fit-content;
+
+ display: flex;
+ align-items: center;
+ gap: 0 16px;
+
+ margin: 24px 0;
+ padding: 8px;
+
+ border: 1px solid var(--brand-color);
+ border-radius: 4px;
+
+}
\ No newline at end of file
diff --git a/app/media/[id]/components/Reviews/components/AnchorTag/index.tsx b/app/media/[id]/components/Reviews/components/AnchorTag/index.tsx
new file mode 100644
index 00000000..063bb472
--- /dev/null
+++ b/app/media/[id]/components/Reviews/components/AnchorTag/index.tsx
@@ -0,0 +1,20 @@
+"use client";
+import React from "react";
+import OutsideLinkSvg from "@/public/assets/box-arrow-up-right.svg";
+import { motion } from "framer-motion";
+import styles from "./component.module.css";
+
+function AnchorTag({ reviewId }: { reviewId: number }) {
+ return (
+
+ Continue on AniList
+
+ );
+}
+
+export default AnchorTag;
diff --git a/app/media/[id]/components/Reviews/components/ReviewRating/component.module.css b/app/media/[id]/components/Reviews/components/ReviewRating/component.module.css
new file mode 100644
index 00000000..fa7e9fea
--- /dev/null
+++ b/app/media/[id]/components/Reviews/components/ReviewRating/component.module.css
@@ -0,0 +1,36 @@
+.rating_container {
+
+ padding: 8px;
+ text-align: center;
+
+ width: 120px;
+
+ font-weight: 600;
+
+ font-family: var(--font-family-sans-serif);
+ font-size: var(--font-size--p);
+}
+
+.rating_container[data-bcg-color=good] {
+
+ background-color: #8BC34A;
+
+}
+
+.rating_container[data-bcg-color=medium] {
+
+ background-color: #FF9800;
+
+}
+
+.rating_container[data-bcg-color=bad] {
+
+ background-color: #e70027;
+
+}
+
+.rating_container[data-bcg-color=null] {
+
+ background-color: #ffffff;
+
+}
\ No newline at end of file
diff --git a/app/media/[id]/components/Reviews/components/ReviewRating/index.tsx b/app/media/[id]/components/Reviews/components/ReviewRating/index.tsx
new file mode 100644
index 00000000..66a4a0aa
--- /dev/null
+++ b/app/media/[id]/components/Reviews/components/ReviewRating/index.tsx
@@ -0,0 +1,22 @@
+import React from "react";
+import styles from "./component.module.css";
+
+function ReviewRating({ ratingScore }: { ratingScore: number }) {
+ function getBcgColorByRatingScore() {
+ if (ratingScore >= 75) return "good";
+ else if (ratingScore >= 50) return "medium";
+ else if (ratingScore <= 49) return "bad";
+
+ return "null";
+ }
+
+ const bcgComponentColor = getBcgColorByRatingScore();
+
+ return (
+
+ {ratingScore}/100
+
+ );
+}
+
+export default ReviewRating;
diff --git a/app/media/[id]/components/Reviews/index.tsx b/app/media/[id]/components/Reviews/index.tsx
new file mode 100644
index 00000000..5dd275cc
--- /dev/null
+++ b/app/media/[id]/components/Reviews/index.tsx
@@ -0,0 +1,57 @@
+import Image from "next/image";
+import React from "react";
+import styles from "./component.module.css";
+import { MediaDataFullInfo } from "@/app/ts/interfaces/anilistMediaData";
+import parse from "html-react-parser";
+import QuoteSvg from "@/public/assets/quote.svg";
+import AnchorTag from "./components/AnchorTag";
+import ReviewRating from "./components/ReviewRating";
+
+function Reviews({
+ reviews,
+}: {
+ reviews: MediaDataFullInfo["reviews"]["nodes"];
+}) {
+ return (
+
+
REVIEWS
+
+
+ {reviews.slice(0, 3).map((review) => (
+
+
+
+
+
+
+
+
{review.user.name}
+
+
+
+
+
+ {review.summary}
+
+
+
+
+
+
{parse(review.body) || "No review"}
+
+
+
+
+
+ ))}
+
+
+ );
+}
+
+export default Reviews;
diff --git a/app/media/[id]/page.tsx b/app/media/[id]/page.tsx
index 08e182f6..156177b1 100644
--- a/app/media/[id]/page.tsx
+++ b/app/media/[id]/page.tsx
@@ -14,8 +14,8 @@ import { convertFromUnix, getMediaReleaseDate } from "@/app/lib/formatDateUnix";
import { getMediaInfoOnIMDB } from "@/app/api/consumet/consumetImdb";
import { ImdbEpisode, ImdbMediaInfo } from "@/app/ts/interfaces/imdb";
import MediaRelatedContainer from "./components/MediaRelatedContainer";
-import CommentsSection from "../../components/CommentsSection";
import PageHeading from "./components/PageHeading";
+import Reviews from "./components/Reviews";
export const revalidate = 43200; // revalidate cached data every 12 hours
@@ -278,12 +278,10 @@ export default async function MediaPage({
)}
- {/* COMMENTS SECTION */}
-
-