diff --git a/.env.example b/.env.example index c916b0ec..3c40a583 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,5 @@ -NEXT_PUBLIC_API_KEY = - +API_KEY = NEXT_PUBLIC_READ_ACCESS_TOKEN = - NEXTAUTH_SECRET = -NEXT_PUBLIC_MS_CLARITY_TAG = NEXTAUTH_URL="http://localhost:3000" \ No newline at end of file diff --git a/.eslintrc.json b/.eslintrc.json index bd2c5078..bf6393d0 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,27 +1,35 @@ { - "extends": "next/core-web-vitals", + "extends": ["next/core-web-vitals", "plugin:@typescript-eslint/recommended"], + "parser": "@typescript-eslint/parser", + "env": { + "browser": true, + "es2021": true + }, "parserOptions": { "ecmaVersion": 2021, "sourceType": "module" }, - "env": { - "browser": true, - "es2021": true, - "node": true - }, "rules": { - "no-unused-vars": "error", - "no-undef": "error", + "@typescript-eslint/no-unused-vars": "error", "no-duplicate-imports": "error", "import/order": [ "error", { - "groups": ["builtin", "external", "parent", "sibling", "index"], + "groups": ["type", "builtin", "external", "internal", "parent", "sibling", "index"], "alphabetize": { "order": "asc", "caseInsensitive": true - } + }, + "newlines-between": "always" } ] - } + }, + "overrides": [ + { + "files": ["*.js", "*.jsx"], + "rules": { + "no-undef": "error" // Apply this rule only to JS/JSX files + } + } + ] } diff --git a/.gitignore b/.gitignore index 1437c53f..da10409c 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,7 @@ yarn-error.log* # vercel .vercel + +# pwa +public/sw.js +public/workbox-*.js diff --git a/.nvmrc b/.nvmrc index 43bff1f8..805efa9f 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20.9.0 \ No newline at end of file +20.14.0 \ No newline at end of file diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..b716a52e --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +/.next +/*-lock.* \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 00000000..5629e1dd --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,8 @@ +{ + "plugins": ["prettier-plugin-tailwindcss"], + "jsxSingleQuote": true, + "bracketSameLine": true, + "bracketSpacing": true, + "trailingComma": "none", + "printWidth": 150 +} diff --git a/README.md b/README.md index 7d9cd79c..5b9af823 100644 --- a/README.md +++ b/README.md @@ -4,4 +4,4 @@ ## Previews -![](/public/Images/ShowCase.webp) +![](/public/images/ShowCase.webp) diff --git a/jsconfig.json b/jsconfig.json deleted file mode 100644 index 738fed44..00000000 --- a/jsconfig.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "compilerOptions": { - "allowSyntheticDefaultImports": false, - "baseUrl": "./", - "paths": { - "components/*": ["src/components/*"], - "images/*": ["public/Images/*"], - "styles/*": ["src/styles/*"], - "theme/*": ["src/theme/*"], - "globals/*": ["src/globals/*"], - "hooks/*": ["src/hooks/*"], - "api/*": ["src/apiEndpoints/*"], - "Store/*": ["src/Store/*"] - } - }, - "exclude": ["node_modules"] -} diff --git a/next-env.d.ts b/next-env.d.ts new file mode 100644 index 00000000..52e831b4 --- /dev/null +++ b/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/next.config.js b/next.config.ts similarity index 77% rename from next.config.js rename to next.config.ts index 3684e56e..a159cddd 100644 --- a/next.config.js +++ b/next.config.ts @@ -1,13 +1,19 @@ -const withPWA = require("next-pwa")({ +import { NextConfig } from "next"; + +/* eslint-disable @typescript-eslint/no-var-requires */ +const withPWA = require("@ducanh2912/next-pwa").default({ dest: "public", + reloadOnOnline: true, register: true, - skipWaiting: true, - disable: process.env.NODE_ENV === "development" + workboxOptions: { + disableDevLogs: true, + exclude: [/dynamic-css-manifest\.json$/] + } }); const cspHeader = ` - default-src *; - script-src 'self' 'unsafe-eval' 'unsafe-inline' https://www.clarity.ms/; + default-src * 'unsafe-inline' 'unsafe-eval' data: blob:; + script-src * 'unsafe-inline' 'unsafe-eval' data: blob:; style-src 'self' 'unsafe-inline'; img-src * data:; font-src 'self' data:; @@ -19,7 +25,7 @@ const cspHeader = ` upgrade-insecure-requests; `; -const nextConfig = { +const nextConfig: NextConfig = { async headers() { return [ { diff --git a/package.json b/package.json index b3f2c9d5..a2cf1fce 100644 --- a/package.json +++ b/package.json @@ -5,29 +5,52 @@ "dev": "next dev", "build": "next build", "start": "next start", - "lint": "next lint", - "lint-fix": "eslint --fix --ext .js,.jsx ." + "lint": "next lint && prettier --check .", + "lint-fix": "eslint --fix --ext .js,.jsx,.ts,.tsx . && prettier --write \"src/**/*.{js,jsx,ts,tsx,json,css,md}\"" }, "dependencies": { + "@ducanh2912/next-pwa": "^10.2.9", + "@radix-ui/react-popover": "^1.1.5", + "@radix-ui/react-select": "^2.1.2", "@suyalcinkaya/gauge": "^0.1.0", + "clsx": "^2.1.1", "extract-colors": "^4.0.2", - "framer-motion": "10.12.2", + "hex-to-rgba": "^2.0.1", "jotai": "^2.9.0", - "next": "^14.2.3", + "keen-slider": "^6.8.6", + "logrocket": "^9.0.2", + "lucide-react": "^0.454.0", + "motion": "^11.14.4", + "next": "^15.1.0", "next-auth": "^4.20.1", - "next-pwa": "^5.5.6", - "react": "18.2.0", - "react-dom": "18.2.0", + "next-navigation-guard": "^0.1.2", + "react": "^19.0.0", + "react-dom": "^19.0.0", "react-icons": "^4.12.0", "react-markdown": "^8.0.3", "rehype-raw": "^7.0.0", - "styled-components": "^6.1.8" + "sonner": "^1.7.2", + "styled-components": "^6.1.8", + "tailwind-merge": "^2.5.2", + "tailwindcss-animate": "^1.0.7", + "throttle-debounce": "^5.0.2", + "typescript": "5.3.3", + "vaul": "^1.1.2" }, "devDependencies": { - "autoprefixer": "^10.4.16", - "eslint": "8.4.1", - "eslint-config-next": "^14.2.3", - "postcss": "^8.4.32", - "tailwindcss": "^3.4.3" + "@types/node": "20.11.16", + "@types/react": "18.2.53", + "@types/react-dom": "18.2.18", + "@types/throttle-debounce": "^5.0.2", + "@typescript-eslint/eslint-plugin": "^6.20.0", + "@typescript-eslint/parser": "^6.20.0", + "autoprefixer": "^10.4.20", + "eslint": "8.56.0", + "eslint-config-next": "14.2.3", + "postcss": "^8.4.49", + "prettier": "^3.2.5", + "prettier-plugin-tailwindcss": "^0.5.3", + "tailwindcss": "^3.4.15", + "webpack": "^5.98.0" } } diff --git a/postcss.config.js b/postcss.config.js index 33ad091d..5cbc2c7d 100644 --- a/postcss.config.js +++ b/postcss.config.js @@ -1,6 +1,6 @@ module.exports = { plugins: { tailwindcss: {}, - autoprefixer: {}, - }, -} + autoprefixer: {} + } +}; diff --git a/public/Images/profileBG.jpg b/public/Images/profileBG.jpg index 988e9913..8572882c 100644 Binary files a/public/Images/profileBG.jpg and b/public/Images/profileBG.jpg differ diff --git a/public/Images/transparent.png b/public/Images/transparent.png new file mode 100644 index 00000000..04cc712f Binary files /dev/null and b/public/Images/transparent.png differ diff --git a/public/images/DefaultAvatar.png b/public/images/DefaultAvatar.png new file mode 100644 index 00000000..6d5b5212 Binary files /dev/null and b/public/images/DefaultAvatar.png differ diff --git a/public/images/DefaultBackdrop.png b/public/images/DefaultBackdrop.png new file mode 100644 index 00000000..67849e6c Binary files /dev/null and b/public/images/DefaultBackdrop.png differ diff --git a/public/images/DefaultImage.png b/public/images/DefaultImage.png new file mode 100644 index 00000000..1150826e Binary files /dev/null and b/public/images/DefaultImage.png differ diff --git a/public/images/Hex.webp b/public/images/Hex.webp new file mode 100644 index 00000000..19751b7f Binary files /dev/null and b/public/images/Hex.webp differ diff --git a/public/images/ShowCase.webp b/public/images/ShowCase.webp new file mode 100644 index 00000000..b1b9d1f2 Binary files /dev/null and b/public/images/ShowCase.webp differ diff --git a/public/images/lightsIn.png b/public/images/lightsIn.png new file mode 100644 index 00000000..21cf49b1 Binary files /dev/null and b/public/images/lightsIn.png differ diff --git a/public/images/lightsOut.png b/public/images/lightsOut.png new file mode 100644 index 00000000..a18ae0c5 Binary files /dev/null and b/public/images/lightsOut.png differ diff --git a/public/images/posters.webp b/public/images/posters.webp new file mode 100644 index 00000000..e3dd4063 Binary files /dev/null and b/public/images/posters.webp differ diff --git a/public/images/profileBG.jpg b/public/images/profileBG.jpg new file mode 100644 index 00000000..8572882c Binary files /dev/null and b/public/images/profileBG.jpg differ diff --git a/public/images/transparent.png b/public/images/transparent.png new file mode 100644 index 00000000..04cc712f Binary files /dev/null and b/public/images/transparent.png differ diff --git a/public/images/watch-providers.webp b/public/images/watch-providers.webp new file mode 100644 index 00000000..6a652606 Binary files /dev/null and b/public/images/watch-providers.webp differ diff --git a/public/manifest.json b/public/manifest.json index 7b94b58f..54420b43 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,12 +1,13 @@ { "name": "Cinephiled", "short_name": "Cinephiled", + "description": "Cinephiled - A progressive web app (PWA) to preview any movie or tv show with reviews, ratings, description and posters. Acting as a TMDB client, Cinephiled gives you access to login into your TMDB account and add movies or tv shows to your watchlist, set as favorites, rate and get personalized recommendations.", "theme_color": "#121212", "background_color": "#121212", "display": "standalone", + "orientation": "portrait", "scope": "/", "start_url": "/", - "icons": [ { "src": "maskable.png", @@ -18,5 +19,6 @@ { "src": "logo256.png", "sizes": "256x256", "type": "image/png" }, { "src": "logo384.png", "sizes": "384x384", "type": "image/png" }, { "src": "logo512.png", "sizes": "512x512", "type": "image/png" } - ] + ], + "categories": ["entertainment", "movies"] } diff --git a/public/robots.txt b/public/robots.txt index 84b4bd9e..ff849305 100644 --- a/public/robots.txt +++ b/public/robots.txt @@ -2,6 +2,7 @@ User-agent: * Disallow: /movies/ Disallow: /tv/ Disallow: /person/ +Disallow: /collections/ Disallow: /*.jpg$ Disallow: /*.jpeg$ Disallow: /*.png$ @@ -11,7 +12,6 @@ Disallow: /*.webp$ Disallow: /*.svg$ Disallow: /en/ Disallow: /m/ -Disallow: /collection/ # Allow these images Allow: /logo192.png diff --git a/public/sitemap.xml b/public/sitemap.xml index 3df16f53..4f8635b8 100644 --- a/public/sitemap.xml +++ b/public/sitemap.xml @@ -7,241 +7,364 @@ - - https://cinephiled.vercel.app/ - 2024-05-14T19:41:04+00:00 - 1.00 - - - https://cinephiled.vercel.app/explore - 2024-05-14T19:41:04+00:00 - 0.80 - - - https://cinephiled.vercel.app/about - 2024-05-14T19:41:04+00:00 - 0.80 - - - https://cinephiled.vercel.app/login - 2024-05-14T19:41:04+00:00 - 0.80 - - - https://cinephiled.vercel.app/genre/28-Action/movies - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/12-Adventure/movies - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/16-Animation/movies - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/35-Comedy/movies - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/80-Crime/movies - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/99-Documentary/movies - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/18-Drama/movies - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/10751-Family/movies - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/14-Fantasy/movies - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/36-History/movies - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/27-Horror/movies - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/10402-Music/movies - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/9648-Mystery/movies - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/10749-Romance/movies - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/878-Science-Fiction/movies - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/10770-TV-Movie/movies - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/53-Thriller/movies - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/10752-War/movies - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/37-Western/movies - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/10759-Action-Adventure/tv - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/16-Animation/tv - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/35-Comedy/tv - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/80-Crime/tv - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/99-Documentary/tv - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/18-Drama/tv - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/10751-Family/tv - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/10762-Kids/tv - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/9648-Mystery/tv - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/10763-News/tv - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/10764-Reality/tv - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/10765-Sci-Fi-Fantasy/tv - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/10766-Soap/tv - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/10767-Talk/tv - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/10768-War-Politics/tv - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/genre/37-Western/tv - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/watch-providers - 2024-05-14T19:41:04+00:00 - 0.64 - - - https://cinephiled.vercel.app/watch-providers/8-Netflix/movies?watchregion=GB - 2024-05-14T19:41:04+00:00 - 0.51 - - - https://cinephiled.vercel.app/watch-providers/337-Disney-Plus/movies?watchregion=GB - 2024-05-14T19:41:04+00:00 - 0.51 - - - https://cinephiled.vercel.app/watch-providers/9-Amazon-Prime-Video/movies?watchregion=GB - 2024-05-14T19:41:04+00:00 - 0.51 - - - https://cinephiled.vercel.app/watch-providers/350-Apple-TV-Plus/movies?watchregion=GB - 2024-05-14T19:41:04+00:00 - 0.51 - - - https://cinephiled.vercel.app/watch-providers/2-Apple-TV/movies?watchregion=GB - 2024-05-14T19:41:04+00:00 - 0.51 - - - https://cinephiled.vercel.app/watch-providers/283-Crunchyroll/movies?watchregion=GB - 2024-05-14T19:41:04+00:00 - 0.51 - - - https://cinephiled.vercel.app/watch-providers/10-Amazon-Video/movies?watchregion=GB - 2024-05-14T19:41:04+00:00 - 0.51 - - - + + https://cinephiled.vercel.app/ + 2025-02-08T09:26:48+00:00 + 1.00 + + + https://cinephiled.vercel.app/explore + 2025-02-08T09:26:48+00:00 + 0.80 + + + https://cinephiled.vercel.app/about + 2025-02-08T09:26:13+00:00 + 0.80 + + + https://cinephiled.vercel.app/login + 2025-02-08T09:26:48+00:00 + 0.80 + + + https://cinephiled.vercel.app/genres/28-Action/movies + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/12-Adventure/movies + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/16-Animation/movies + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/35-Comedy/movies + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/80-Crime/movies + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/99-Documentary/movies + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/18-Drama/movies + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/10751-Family/movies + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/14-Fantasy/movies + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/36-History/movies + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/27-Horror/movies + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/10402-Music/movies + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/9648-Mystery/movies + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/10749-Romance/movies + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/878-Science-Fiction/movies + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/10770-TV-Movie/movies + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/53-Thriller/movies + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/10752-War/movies + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/37-Western/movies + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/10759-Action-Adventure/tv + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/16-Animation/tv + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/35-Comedy/tv + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/80-Crime/tv + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/99-Documentary/tv + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/18-Drama/tv + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/10751-Family/tv + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/10762-Kids/tv + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/9648-Mystery/tv + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/10763-News/tv + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/10764-Reality/tv + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/10765-Sci-Fi-Fantasy/tv + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/10766-Soap/tv + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/10767-Talk/tv + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/10768-War-Politics/tv + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/genres/37-Western/tv + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/watch-providers + 2025-02-08T09:26:48+00:00 + 0.64 + + + https://cinephiled.vercel.app/watch-providers/8-Netflix/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/337-Disney-Plus/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/9-Amazon-Prime-Video/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/350-Apple-TV-Plus/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/2-Apple-TV/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/35-Rakuten-TV/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/283-Crunchyroll/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/10-Amazon-Video/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/531-Paramount-Plus/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/103-Channel-4/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/29-Sky-Go/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/1968-Crunchyroll-Amazon-Channel/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/38-BBC-iPlayer/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/11-MUBI/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/3-Google-Play-Movies/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/224-BFI-Player/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/130-Sky-Store/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/68-Microsoft-Store/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/613-Freevee/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/189-Curzon-Home-Cinema/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/41-ITVX/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/192-YouTube/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/99-Shudder/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/529-ARROW/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/596-Arrow-Video-Amazon-Channel/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/100-GuideDoc/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/175-Netflix-Kids/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/188-YouTube-Premium/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/287-BFI-Player-Amazon-Channel/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/333-My5/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/197-BritBox-Amazon-Channel/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + + + https://cinephiled.vercel.app/watch-providers/201-MUBI-Amazon-Channel/movies?watchregion=GB + 2025-02-08T09:26:48+00:00 + 0.51 + \ No newline at end of file diff --git a/src/Store/ListsContext.js b/src/Store/ListsContext.js index 1e7a4c58..03fe2089 100644 --- a/src/Store/ListsContext.js +++ b/src/Store/ListsContext.js @@ -1,10 +1,13 @@ -import { apiEndpoints } from "globals/constants"; import { createContext, useContext, useEffect, useState } from "react"; -import { fetchOptions } from "src/utils/helper"; + +import { apiEndpoints } from "data/apiEndpoints"; +import { fetchOptions } from "utils/helper"; + import { useUserContext } from "./UserContext"; const ListsContext = createContext({ lists: [], + loading: true, updateList: () => {} }); @@ -20,12 +23,14 @@ export const useListsContext = () => { const ListsContextProvider = ({ children }) => { const [lists, setLists] = useState([]); + const [loading, setLoading] = useState(true); const { userInfo } = useUserContext(); useEffect(() => { const abortController = new AbortController(); const fetchAllLists = async ({ page }) => { + setLoading(true); try { const res = await fetch( apiEndpoints.lists.getLists({ accountId: userInfo?.accountId, pageQuery: page }), @@ -45,6 +50,8 @@ const ListsContextProvider = ({ children }) => { } } catch { setLists([]); + } finally { + setLoading(false); } }; @@ -58,11 +65,7 @@ const ListsContextProvider = ({ children }) => { }; }, [userInfo?.accessToken, userInfo?.accountId]); - return ( - - {children} - - ); + return {children}; }; export default ListsContextProvider; diff --git a/src/Store/MediaContext.js b/src/Store/MediaContext.js index 95d19782..c0d2c40d 100644 --- a/src/Store/MediaContext.js +++ b/src/Store/MediaContext.js @@ -1,5 +1,7 @@ -import { getFavorites, getRated, getRecommendations, getWatchlist } from "api/user"; import { createContext, useCallback, useContext, useEffect, useState } from "react"; + +import { getFavorites, getRated, getRecommendations, getWatchlist } from "apiRoutes/user"; + import { useUserContext } from "./UserContext"; const defaultState = { @@ -91,9 +93,7 @@ const MediaContextProvider = ({ children }) => { if (userInfo?.accountId) { setState((prev) => ({ ...prev, isLoading: true })); - const fetchPromises = allEndpoints.map((media) => - fetchData({ ...media, signal: abortController.signal }) - ); + const fetchPromises = allEndpoints.map((media) => fetchData({ ...media, signal: abortController.signal })); Promise.all(fetchPromises) .then(() => { @@ -125,11 +125,7 @@ const MediaContextProvider = ({ children }) => { } }, []); - return ( - - {children} - - ); + return {children}; }; export default MediaContextProvider; diff --git a/src/Store/UserContext.js b/src/Store/UserContext.js index 9aecb83d..cee7905c 100644 --- a/src/Store/UserContext.js +++ b/src/Store/UserContext.js @@ -1,10 +1,30 @@ -import { apiEndpoints } from "globals/constants"; +import LogRocket from "logrocket"; import { useSession, signOut } from "next-auth/react"; import { useState, createContext, useEffect, useContext } from "react"; -import { fetchOptions } from "src/utils/helper"; + +import { apiEndpoints } from "data/apiEndpoints"; +import { fetchOptions } from "utils/helper"; const UserContext = createContext({ - userInfo: {}, + userInfo: { + avatar: { + gravatar: { + hash: "" + }, + tmdb: { + avatar_path: null + } + }, + id: "", + iso_639_1: "", + iso_3166_1: "", + name: "", + include_adult: false, + username: "", + accountId: "", + accessToken: "", + status: "" + }, setUserInfo: () => {} }); @@ -22,6 +42,17 @@ export default function UserContextProvider({ children }) { const { status, data } = useSession(); const [userInfo, setUserInfo] = useState({}); + // identify user in LogRocket + useEffect(() => { + if (userInfo?.accountId && userInfo?.name) { + LogRocket.identify(userInfo?.username, { + id: userInfo?.accountId, + name: userInfo?.name, + username: userInfo?.username + }); + } + }, [userInfo?.accountId, userInfo?.name, userInfo?.username]); + useEffect(() => { const getUserInfo = async () => { const profileRes = await fetch( diff --git a/src/apiEndpoints/auth.js b/src/apiRoutes/auth.js similarity index 66% rename from src/apiEndpoints/auth.js rename to src/apiRoutes/auth.js index 78e464bf..19f8d515 100644 --- a/src/apiEndpoints/auth.js +++ b/src/apiRoutes/auth.js @@ -1,9 +1,10 @@ -import { apiEndpoints } from "globals/constants"; import { useRouter } from "next/router"; import { signOut } from "next-auth/react"; import { useState } from "react"; -import { fetchOptions } from "src/utils/helper"; + +import { apiEndpoints } from "data/apiEndpoints"; import { useUserContext } from "Store/UserContext"; +import { fetchOptions } from "utils/helper"; // login hook export const useLogin = () => { @@ -18,22 +19,27 @@ export const useLogin = () => { setIsWaiting(true); clearError(); - // generate temporary token (validity: 15 mins) - const requestTokenRes = await fetch( - apiEndpoints.auth.requestToken, - fetchOptions({ - method: "POST", - body: { redirect_to: `${window.location.href}?approved=true` } - }) - ); - - const requestTokenData = await requestTokenRes.json(); - const { request_token, success } = requestTokenData; - - if (request_token && success) { - sessionStorage.setItem("request_token", request_token); - window.open(`https://www.themoviedb.org/auth/access?request_token=${request_token}`, "_self"); - } else { + try { + // generate temporary token (validity: 15 mins) + const requestTokenRes = await fetch( + apiEndpoints.auth.requestToken, + fetchOptions({ + method: "POST", + body: { redirect_to: `${window.location.href}?approved=true` } + }) + ); + + const requestTokenData = await requestTokenRes.json(); + const { request_token, success } = requestTokenData; + + if (request_token && success) { + sessionStorage.setItem("request_token", request_token); + window.open(`https://www.themoviedb.org/auth/access?request_token=${request_token}`, "_self"); + } else { + setIsWaiting(false); + setError({ error: true, message: "Server Error, Try again later" }); + } + } catch (error) { setIsWaiting(false); setError({ error: true, message: "Server Error, Try again later" }); } diff --git a/src/apiEndpoints/user.js b/src/apiRoutes/user.js similarity index 92% rename from src/apiEndpoints/user.js rename to src/apiRoutes/user.js index a96cdb13..5d9e20d9 100644 --- a/src/apiEndpoints/user.js +++ b/src/apiRoutes/user.js @@ -1,6 +1,7 @@ -import { apiEndpoints } from "globals/constants"; import { getSession } from "next-auth/react"; -import { fetchOptions } from "src/utils/helper"; + +import { apiEndpoints } from "data/apiEndpoints"; +import { fetchOptions } from "utils/helper"; // GET requsts @@ -83,7 +84,7 @@ export const getRecommendations = async ({ mediaType, pageQuery, accountId, toke export const getListItemStatus = async ({ listId, mediaType, mediaId, signal }) => { const { user: { accessToken } - } = await getSession(); + } = await getSession({ broadcast: false }); const res = await fetch( "/api/getItemStatus", @@ -132,7 +133,7 @@ export const getCountryCode = async (ip) => { export const setFavorite = async ({ mediaType, mediaId, favoriteState }) => { const { user: { accountId, accessToken } - } = await getSession(); + } = await getSession({ broadcast: false }); if (accountId) { const favorite = await fetch( @@ -163,7 +164,7 @@ export const setFavorite = async ({ mediaType, mediaId, favoriteState }) => { export const addToWatchlist = async ({ mediaType, mediaId, watchlistState }) => { const { user: { accountId, accessToken } - } = await getSession(); + } = await getSession({ broadcast: false }); if (accountId) { const watchlist = await fetch( @@ -194,7 +195,7 @@ export const addToWatchlist = async ({ mediaType, mediaId, watchlistState }) => export const setRating = async ({ mediaType, mediaId, rating }) => { const { user: { accountId, accessToken } - } = await getSession(); + } = await getSession({ broadcast: false }); if (accountId) { const rated = await fetch( @@ -224,7 +225,7 @@ export const setRating = async ({ mediaType, mediaId, rating }) => { export const createList = async ({ listData }) => { const { user: { accessToken } - } = await getSession(); + } = await getSession({ broadcast: false }); const res = await fetch( apiEndpoints.lists.createList, @@ -244,7 +245,7 @@ export const createList = async ({ listData }) => { export const updateListItems = async ({ id, itemsData, method }) => { const { user: { accessToken } - } = await getSession(); + } = await getSession({ broadcast: false }); const res = await fetch( apiEndpoints.lists.listItems({ id: id }), @@ -265,7 +266,7 @@ export const updateListItems = async ({ id, itemsData, method }) => { export const updateList = async ({ id, listData }) => { const { user: { accessToken } - } = await getSession(); + } = await getSession({ broadcast: false }); const res = await fetch( apiEndpoints.lists.updateList({ id: id }), @@ -286,7 +287,7 @@ export const updateList = async ({ id, listData }) => { export const deleteRating = async ({ mediaType, mediaId }) => { const { user: { accountId, accessToken } - } = await getSession(); + } = await getSession({ broadcast: false }); if (accountId) { const deleted = await fetch( @@ -313,7 +314,7 @@ export const deleteRating = async ({ mediaType, mediaId }) => { export const deleteList = async ({ id }) => { const { user: { accessToken } - } = await getSession(); + } = await getSession({ broadcast: false }); const res = await fetch( apiEndpoints.lists.updateList({ id: id }), diff --git a/src/components/Backdrops/Backdrops.js b/src/components/Backdrops/Backdrops.js index 4eef0527..aca632b9 100644 --- a/src/components/Backdrops/Backdrops.js +++ b/src/components/Backdrops/Backdrops.js @@ -1,10 +1,13 @@ -import DownloadMediaButton from "components/DownloadMediaButton/DownloadMediaButton"; -import PlaceholderText from "components/PlaceholderText"; -import { blurPlaceholder } from "globals/constants"; import Image from "next/image"; import { Fragment } from "react"; + +import PlaceholderText from "components/PlaceholderText"; +import DownloadMediaButton from "components/Shared/DownloadMediaButton"; +import { blurPlaceholder } from "data/global"; + import { BackdropsImg, BackdropsImgContainer, BackdropsWrapper } from "./BackdropsStyles"; +// redundant const Backdrops = ({ backdrops }) => { return ( diff --git a/src/components/Backdrops/BackdropsStyles.js b/src/components/Backdrops/BackdropsStyles.js index 9b3266e3..cf6fcf1b 100644 --- a/src/components/Backdrops/BackdropsStyles.js +++ b/src/components/Backdrops/BackdropsStyles.js @@ -6,7 +6,7 @@ export const BackdropsWrapper = styled.div` place-items: center; gap: clamp(20px, 3vw, 32px); - @media only ${(props) => props.theme.breakpoints.sm} { + @media only ${({ theme }) => theme.breakpoints.sm} { grid-template-columns: 1fr; } `; @@ -16,7 +16,9 @@ export const BackdropsImgContainer = styled.div` aspect-ratio: 1.68; border-radius: 12px; overflow: hidden; - box-shadow: 0px 4px 5px 0px hsla(0, 0%, 0%, 0.14), 0px 1px 10px 0px hsla(0, 0%, 0%, 0.12), + box-shadow: + 0px 4px 5px 0px hsla(0, 0%, 0%, 0.14), + 0px 1px 10px 0px hsla(0, 0%, 0%, 0.12), 0px 2px 4px -1px hsla(0, 0%, 0%, 0.2); `; @@ -27,7 +29,7 @@ export const BackdropsImg = styled.div` .media { transition: transform 0.25s cubic-bezier(0.79, 0.14, 0.15, 0.86); - @media ${(props) => props.theme.breakpoints.hover} { + @media ${({ theme }) => theme.breakpoints.hover} { &:hover { transform: scale(1.05); } diff --git a/src/components/Breadcrumbs/Breadcrumbs.js b/src/components/Breadcrumbs/Breadcrumbs.js deleted file mode 100644 index d39ca0e8..00000000 --- a/src/components/Breadcrumbs/Breadcrumbs.js +++ /dev/null @@ -1,43 +0,0 @@ -import Link from "next/link"; - -const Breadcrumbs = ({ links }) => { - return ( - - ); -}; - -export default Breadcrumbs; diff --git a/src/components/Breadcrumbs/Breadcrumbs.jsx b/src/components/Breadcrumbs/Breadcrumbs.jsx new file mode 100644 index 00000000..b5df9ddb --- /dev/null +++ b/src/components/Breadcrumbs/Breadcrumbs.jsx @@ -0,0 +1,37 @@ +import Link from "next/link"; + +import FlexBox from "components/UI/FlexBox"; + +const Breadcrumbs = ({ links }) => { + return ( + + ); +}; + +export default Breadcrumbs; diff --git a/src/components/Cast/Cast.js b/src/components/Cast/Cast.js index 92c854ad..1723f0ac 100644 --- a/src/components/Cast/Cast.js +++ b/src/components/Cast/Cast.js @@ -1,13 +1,15 @@ -import { Span } from "components/MovieInfo/MovieDetailsStyles"; -import PlaceholderText from "components/PlaceholderText"; -import { motion } from "framer-motion"; -import { blurPlaceholder } from "globals/constants"; +import { motion } from "motion/react"; import Image from "next/image"; import Link from "next/link"; import { useRouter } from "next/router"; import { Fragment, useRef } from "react"; import { BsChevronRight } from "react-icons/bs"; -import { getCleanTitle } from "src/utils/helper"; + +import { Span } from "components/MovieInfo/MovieDetailsStyles"; +import PlaceholderText from "components/PlaceholderText"; +import { blurPlaceholder } from "data/global"; +import { getCleanTitle } from "utils/helper"; + import { CastGrid, CastImg, CastWrapper, SeeMore } from "./CastStyles"; const Cast = ({ cast, showFullCastLink = false, isSearchGrid = false }) => { @@ -29,11 +31,7 @@ const Cast = ({ cast, showFullCastLink = false, isSearchGrid = false }) => { whileTap={{ scale: 0.95 }}> cast-image { -
+
{isSearchGrid ? ( {item.name} - - {item.known_for_department} - + {item.known_for_department} ) : ( - - {item?.roles?.[0]?.character ?? item?.character} - + {item?.roles?.[0]?.character ?? item?.character} {item.name} - {item?.episode_count ? ( - - {item?.episode_count} episodes - - ) : null} + {item?.episode_count ? {item?.episode_count} episodes : null} )}
@@ -82,7 +72,7 @@ const Cast = ({ cast, showFullCastLink = false, isSearchGrid = false }) => { - Full Cast + Full Cast ) : null} diff --git a/src/components/Cast/CastPage.js b/src/components/Cast/CastPage.js index d872388f..ebce5070 100644 --- a/src/components/Cast/CastPage.js +++ b/src/components/Cast/CastPage.js @@ -1,10 +1,11 @@ -import DominantColor from "components/DominantColor/DominantColor"; +import { AnimatePresence, motion } from "motion/react"; +import { Fragment, useRef, useState } from "react"; + import { HeroInfoTitle } from "components/MovieInfo/MovieDetailsStyles"; import PlaceholderText from "components/PlaceholderText"; -import { AnimatePresence, motion } from "framer-motion"; -import { Fragment, useRef, useState } from "react"; -import { framerTabVariants } from "src/utils/helper"; +import DominantColor from "components/Shared/DominantColor/DominantColor"; import { ModulesWrapper } from "styles/GlobalComponents"; + import Cast from "./Cast"; const CastPage = ({ media: { title, year, poster }, cast }) => { @@ -18,11 +19,11 @@ const CastPage = ({ media: { title, year, poster }, cast }) => { return; } + // todo: use throttle here timeoutRef.current = setTimeout(() => { const searchValue = e.target.value.toLowerCase(); const filteredCast = cast.filter( - ({ name, character }) => - name.toLowerCase().includes(searchValue) || character.toLowerCase().includes(searchValue) + ({ name, character }) => name.toLowerCase().includes(searchValue) || character.toLowerCase().includes(searchValue) ); setFilteredCast(filteredCast); }, 300); @@ -31,21 +32,21 @@ const CastPage = ({ media: { title, year, poster }, cast }) => { return (
- + -
+
{title} ({year})
-
-

{`Cast (${cast?.length})`}

+
+

{`Cast (${cast?.length})`}

@@ -55,13 +56,7 @@ const CastPage = ({ media: { title, year, poster }, cast }) => { {filteredCast?.length > 0 ? ( ) : ( - + No results found )} diff --git a/src/components/Cast/CastStyles.js b/src/components/Cast/CastStyles.js index 9d0a8c8c..febfbf91 100644 --- a/src/components/Cast/CastStyles.js +++ b/src/components/Cast/CastStyles.js @@ -8,11 +8,11 @@ export const CastGrid = styled.div` justify-content: center; gap: clamp(20px, 3vw, 32px); - @media only ${(props) => props.theme.breakpoints.sm} { + @media only ${({ theme }) => theme.breakpoints.sm} { grid-template-columns: repeat(3, 1fr); } - @media only ${(props) => props.theme.breakpoints.xs} { + @media only ${({ theme }) => theme.breakpoints.xs} { grid-template-columns: repeat(2, 1fr); } `; @@ -28,9 +28,11 @@ export const CastImg = styled.div` border-radius: 12px; overflow: hidden; - @media ${(props) => props.theme.breakpoints.hover} { + @media ${({ theme }) => theme.breakpoints.hover} { &:hover { - box-shadow: 0px 12px 17px 2px hsla(0, 0%, 0%, 0.14), 0px 5px 22px 4px hsla(0, 0%, 0%, 0.12), + box-shadow: + 0px 12px 17px 2px hsla(0, 0%, 0%, 0.14), + 0px 5px 22px 4px hsla(0, 0%, 0%, 0.12), 0px 7px 8px -4px hsla(0, 0%, 0%, 0.2); } } diff --git a/src/components/CollectionCard/CollectionCard.js b/src/components/CollectionCard/CollectionCard.js index d474fba4..b1d32fd4 100644 --- a/src/components/CollectionCard/CollectionCard.js +++ b/src/components/CollectionCard/CollectionCard.js @@ -1,36 +1,31 @@ -import { blurPlaceholder } from "globals/constants"; import Image from "next/image"; import Link from "next/link"; -import { getCleanTitle } from "src/utils/helper"; + +import { blurPlaceholder } from "data/global"; +import { getCleanTitle } from "utils/helper"; const CollectionCard = ({ collection }) => { return ( -
+
- -
-
+ +
+
collection-poster
-
-

+

+

Part of the
{collection.name}

-

- View Collection -

+

View Collection

diff --git a/src/components/DominantColor/DominantColor.js b/src/components/DominantColor/DominantColor.js deleted file mode 100644 index 9711361c..00000000 --- a/src/components/DominantColor/DominantColor.js +++ /dev/null @@ -1,35 +0,0 @@ -import { motion } from "framer-motion"; -import { usePalette } from "hooks/usePalette"; -import { Fragment } from "react"; -import { Colorful } from "./DominantColorStyles"; - -const DominantColor = ({ - image, - tint = false, - flip = false, - isUsingBackdrop = false, - className = "" -}) => { - const prefix = isUsingBackdrop ? "w533_and_h300_bestv2" : "w300_and_h450_bestv2"; - const { palette, done } = usePalette(image ? `https://image.tmdb.org/t/p/${prefix}${image}` : ""); - - return ( - - {done ? ( - - ) : null} - - ); -}; - -export default DominantColor; diff --git a/src/components/DominantColor/DominantColorStyles.js b/src/components/DominantColor/DominantColorStyles.js deleted file mode 100644 index 6e6a5515..00000000 --- a/src/components/DominantColor/DominantColorStyles.js +++ /dev/null @@ -1,17 +0,0 @@ -import styled from "styled-components"; - -export const Colorful = styled.div` - inset: 0; - background-image: linear-gradient(90deg, #ccbdb4, #392a28d6); - background-image: ${({ palette }) => - `linear-gradient(90deg, ${palette?.[0]}, ${palette?.[1] + "8e"})`}; - - &.tint { - background-image: ${({ flip }) => - `linear-gradient(${flip ? "180deg" : "0deg"}, #ccbdb463, #392a28d6, #121212)`}; - background-image: ${({ palette, flip }) => - `linear-gradient(${flip ? "180deg" : "0deg"}, ${palette?.[0] + "94"}, ${ - palette?.[1] + "8e" - }, #121212)`}; - } -`; diff --git a/src/components/Explore/ExploreStyles.js b/src/components/Explore/ExploreStyles.js index 2320a341..f800b438 100644 --- a/src/components/Explore/ExploreStyles.js +++ b/src/components/Explore/ExploreStyles.js @@ -1,67 +1,23 @@ -import styled from "styled-components"; +import { css } from "styled-components"; -export const PseudoTrack = styled.div` - background-color: rgba(255, 255, 255, 0.2); - height: 2px; - width: calc(100% - 32.1vw); - margin: auto; - position: relative; - top: -4px; - z-index: -10; - - @media only ${({ theme }) => theme.breakpoints.xl} { - display: none; - } -`; - -export const OverFlowWrapper = styled.div` - display: flex; - gap: 1.25rem; - padding-bottom: 1rem; - max-width: 100%; - overflow-x: auto; - padding-right: 1.5rem; - - &::-webkit-scrollbar-track { - margin-inline: 16vw; - } +import { theme } from "theme/theme"; +import { hoverMediaQuery, transition } from "utils/mixins"; - @media only ${({ theme }) => theme.breakpoints.xl} { - padding-bottom: 0px; - - &::-webkit-scrollbar { - display: none; - } - - /* for firefox */ - scrollbar-width: none; - } -`; - -export const GenreBG = styled.a` - min-width: 180px; - width: calc(100% / 6); - flex-shrink: 0; - flex-grow: 1; - border-radius: 8px; +export const genreCardStyles = css` + border-radius: ${theme.borderRadius.lg}; padding: 24px; aspect-ratio: 1/0.5; position: relative; display: grid; place-items: center; - transition: letter-spacing 0.325s cubic-bezier(0.77, 0, 0.175, 1); - background: ${({ theme }) => - `linear-gradient(200deg,${theme.colors.accent1}, ${theme.colors.accent2}, ${theme.colors.accent3})`}; + transition: ${transition({ + property: "filter", + duration: 0.25, + timingFunction: "linear" + })}; + background: ${`linear-gradient(200deg,${theme.colors.accentTertiary}, ${theme.colors.accentPrimary}, ${theme.colors.accentSecondary})`}; overflow: hidden; - .title { - text-align: center; - z-index: 2; - font-weight: 600; - color: white; - font-size: clamp(1.15rem, 1.6vw, 1.75rem); - } - &::after { content: ""; inset: 0; @@ -71,99 +27,9 @@ export const GenreBG = styled.a` z-index: 1; } - &:hover { - letter-spacing: 2px; - } -`; - -export const PostersGrid = styled.div` - display: grid; - position: relative; - width: 100%; - margin-bottom: auto; - grid-template-columns: ${({ $colCount }) => `repeat(${$colCount}, minmax(130px, 205px))`}; - justify-content: center; - gap: 1.25rem; - max-height: 35vh; - overflow: hidden; - mask-image: linear-gradient(to bottom, black 0%, transparent); - pointer-events: none; - user-select: none; - transform: scale(1.01); - - &::after { - content: ""; - inset: 0; - background: ${({ theme }) => - `linear-gradient(200deg,${theme.colors.accent1}, ${theme.colors.accent2}, ${theme.colors.accent3})`}; - opacity: 0.5; - position: absolute; - z-index: 1; - } - - &:not(.alt-grid) { - .poster-wrapper:not(:nth-child(odd)) { - transform: translateY(-100px); - } - } - - @media only ${({ theme }) => theme.breakpoints.sm} { - gap: 0.75rem; - grid-template-columns: repeat(4, minmax(80px, 1fr)); - } -`; - -export const NetwrokDetailsWrapper = styled.div` - width: 100%; - position: relative; - display: grid; - place-items: center; - overflow: hidden; - - & > * { - grid-area: 1 / 1; - } - - .network-info { - position: relative; - z-index: 20; - } - - .details-row { - display: flex; - justify-content: center; - align-items: center; - gap: 32px; - margin-top: 1.5rem; - - @media only ${({ theme }) => theme.breakpoints.sm} { - flex-direction: column; - margin-top: 1rem; - gap: 8px; - } - } - - .link { - text-decoration: underline dotted; - text-underline-offset: 4px; - transition: opacity 0.2s ease-in-out; - + ${hoverMediaQuery()} { &:hover { - opacity: 0.7; - } - } - - .logo-wrapper { - position: relative; - width: 200px; - aspect-ratio: var(--aspectRatio); - - @media only ${({ theme }) => theme.breakpoints.sm} { - width: 140px; - } - - img { - object-fit: contain !important; + filter: saturate(0); } } `; diff --git a/src/components/Explore/Genres.js b/src/components/Explore/Genres.js deleted file mode 100644 index f2997b97..00000000 --- a/src/components/Explore/Genres.js +++ /dev/null @@ -1,48 +0,0 @@ -import { motion } from "framer-motion"; -import Link from "next/link"; -import { Fragment } from "react"; -import { getCleanTitle } from "src/utils/helper"; -import { GenreBG, OverFlowWrapper, PseudoTrack } from "./ExploreStyles"; - -const GenreSection = ({ genres, mediaType }) => { - return ( - -

- {mediaType === "movie" ? "Movie" : "TV"} Genres -

- - - {genres.map((genre) => ( - - - {genre.name} - - - ))} - - -
- ); -}; - -const Genres = ({ movieGenres, tvGenres }) => { - return ( - -
- -
- -
- -
-
- ); -}; - -export default Genres; diff --git a/src/components/Explore/Genres.jsx b/src/components/Explore/Genres.jsx new file mode 100644 index 00000000..55d2d9ee --- /dev/null +++ b/src/components/Explore/Genres.jsx @@ -0,0 +1,93 @@ +import "keen-slider/keen-slider.min.css"; +import { useKeenSlider } from "keen-slider/react"; +import Link from "next/link"; +import { Fragment } from "react"; + +import H3 from "components/UI/Typography/H3"; +import H5 from "components/UI/Typography/H5"; +import { ROUTES } from "data/global"; +import { theme } from "theme/theme"; +import { getNiceName, matches } from "utils/helper"; + +import { genreCardStyles } from "./ExploreStyles"; + +const GenreSection = ({ genres, genreType }) => { + const [sliderRef] = useKeenSlider({ + renderMode: "performance", + dragSpeed: 1.4, + breakpoints: { + "(min-width: 0px)": { + slides: { + perView: 1.75, + spacing: 16 + } + }, + [`(min-width: ${theme.breakpoints.sm})`]: { + slides: { + perView: 2.75, + spacing: 16 + } + }, + [`(min-width: ${theme.breakpoints.lg})`]: { + slides: { + perView: 3.75, + spacing: 20 + } + }, + [`(min-width: ${theme.breakpoints.xl})`]: { + slides: { + perView: 4.75, + spacing: 20 + } + }, + [`(min-width: ${theme.breakpoints["2xl"]})`]: { + slides: { + perView: 5.75, + spacing: 20 + } + }, + [`(min-width: ${theme.breakpoints["3xl"]})`]: { + slides: { + perView: 6.75, + spacing: 20 + } + } + } + }); + + return ( + +

+ {matches(genreType, "movies") ? "Movie" : "TV"} Genres +

+ +
+ {genres.map((genre) => ( + + +
+ {genre.name} +
+
+ + ))} +
+
+ ); +}; + +const Genres = ({ movieGenres, tvGenres }) => { + return ( + +
+ +
+ +
+ +
+
+ ); +}; + +export default Genres; diff --git a/src/components/Explore/NetworkMedia.js b/src/components/Explore/NetworkMedia.js deleted file mode 100644 index a080b473..00000000 --- a/src/components/Explore/NetworkMedia.js +++ /dev/null @@ -1,133 +0,0 @@ -import NetworkMediaGrid from "components/MediaTemplate/TVTemplate"; -import { PostersImg } from "components/Posters/PostersStyles"; -import Select from "components/Select/Select"; -import { apiEndpoints, blurPlaceholder, sortOptions } from "globals/constants"; -import useInfiniteQuery from "hooks/useInfiniteQuery"; -import useSort from "hooks/useSort"; -import Image from "next/image"; -import { Fragment } from "react"; -import { FaLink } from "react-icons/fa"; -import { FaLocationDot } from "react-icons/fa6"; -import { getActiveSortKey } from "src/utils/getSortedItems"; -import { ModulesWrapper } from "styles/GlobalComponents"; -import { NetwrokDetailsWrapper, PostersGrid } from "./ExploreStyles"; - -const NetworkMedia = ({ details, media }) => { - const posters = media.map(({ poster_path }) => - poster_path ? `https://image.tmdb.org/t/p/w185${poster_path}` : "/Images/DefaultImage.png" - ); - - const { sortBy, handleSortSelection } = useSort({ shallow: false }); - - const { list, resetQueryState } = useInfiniteQuery({ - initialPage: 2, - getEndpoint: ({ page }) => - apiEndpoints.network.networkMedia({ id: details.id, pageQuery: page, sortBy }) - }); - - if (posters.length % 2 !== 0 && posters.length > 10) { - posters.pop(); - } - - let colCount; - const postersLength = posters?.length; - - if (postersLength > 10) { - colCount = - Math.ceil(postersLength / 2) % 2 === 0 - ? Math.ceil(postersLength / 2) - : Math.ceil(postersLength / 2) + 1; - } else { - colCount = postersLength; - } - - const networkMediaSortOptions = sortOptions.tmdbOptions.tv; - - const handleSort = (key) => { - handleSortSelection(key); - resetQueryState(); - }; - - return ( - - - {postersLength > 0 ? ( - - {posters.map((poster, i) => ( - - {`${details?.name}-poster`} - - ))} - - ) : null} - -
-
- {`${details?.name}-poster`} -
- -
- {details.name} - {details?.headquarters || details?.origin_country ? ( -
- - {details.headquarters || details.origin_country} -
- ) : null} - {details.homepage ? ( - - ) : null} -
-
-
- - -
- -
- - - {(searchSuggestionsLoading || searchSuggestions?.length > 0) && ( - - {searchSuggestionsLoading ? ( - - ) : ( - - {searchSuggestions.length > 0 ? ( -
- {searchSuggestions.map( - ({ - id, - title, - name, - poster_path, - release_date, - first_air_date, - type - }) => ( -
- itemsHandler({ - item: { - id, - poster_path, - media_type: type - }, - action: "add" - }) - }> -
-
- poster -
-
-

- {type === "tv" ? "TV Show" : "Movie"} -

- -

- {title || name} -

-

- {getReleaseDate(release_date || first_air_date)} -

-
-
-
- ) - )} -
- ) : null} -
- )} -
- )} -
-
- -
- -
- -
- {items?.length > 0 ? ( - - {items.map(({ id, poster_path, media_type }) => ( -
-
- poster -
- - -
- ))} -
- ) : ( - - There are no items added to this list.
Add items to continue. -
- )} -
-
- )} - - - {toastMessage} - - - )} - - ); -}; - -export default AddListItems; diff --git a/src/components/List/ChooseListCover.js b/src/components/List/ChooseListCover.js deleted file mode 100644 index 0210f583..00000000 --- a/src/components/List/ChooseListCover.js +++ /dev/null @@ -1,111 +0,0 @@ -import { updateList } from "api/user"; -import Loading from "components/Loading"; -import { Span } from "components/MovieInfo/MovieDetailsStyles"; -import PlaceholderText from "components/PlaceholderText"; -import Toast, { useToast } from "components/Toast/Toast"; -import { blurPlaceholder } from "globals/constants"; -import Image from "next/image"; -import { useRouter } from "next/router"; -import { Fragment, useState } from "react"; -import { getCleanTitle } from "src/utils/helper"; -import { Button } from "styles/GlobalComponents"; -import useGetListDetails from "./useGetListDetails"; - -const ChooseListCover = ({ id }) => { - const { error, listDetails, loading } = useGetListDetails({ id }); - const { isToastVisible, showToast, toastMessage } = useToast(); - const [selectedCover, setSelectedCover] = useState(); - const router = useRouter(); - - const coverClickHandler = async (cover) => { - const { success } = await updateList({ id, listData: { backdrop_path: cover } }); - - if (!success) { - showToast({ message: "Error updating the list details, please try again later." }); - return; - } - - setSelectedCover(cover); - showToast({ message: "List cover added successfully." }); - }; - - const CTAHandler = () => { - router.push(`/lists/${id}-${getCleanTitle(listDetails?.name)}`); - }; - - return ( - - {loading ? ( - - ) : ( - - {error ? ( -
- Something went wrong. Please try again later. -
- ) : ( -
-
-

{listDetails.name}

- - -
- - {listDetails?.results?.length > 0 ? ( -
- {listDetails.results.map(({ id, backdrop_path, name, title }) => ( -
-
coverClickHandler(backdrop_path)}> - backdrop - - {selectedCover === backdrop_path && ( -
- Selected -
- )} -
- -

{name || title}

-
- ))} -
- ) : ( - - There are no items added to this list yet. - - )} -
- )} -
- )} - - - {toastMessage} - -
- ); -}; - -export default ChooseListCover; diff --git a/src/components/List/CreateList.js b/src/components/List/CreateList.js deleted file mode 100644 index f3aeb8ba..00000000 --- a/src/components/List/CreateList.js +++ /dev/null @@ -1,164 +0,0 @@ -import { createList } from "api/user"; -import { AnimatePresence, motion } from "framer-motion"; -import { apiEndpoints } from "globals/constants"; -import { Fragment, useState } from "react"; -import { fetchOptions, framerTabVariants } from "src/utils/helper"; -import { useListsContext } from "Store/ListsContext"; -import { useUserContext } from "Store/UserContext"; -import { Button } from "styles/GlobalComponents"; -import AddListItems from "./AddListItems"; -import ChooseListCover from "./ChooseListCover"; -import CreateListForm from "./CreateListForm"; - -const steps = [ - { key: 1, title: "List Details" }, - { key: 2, title: "Add Items" }, - { key: 3, title: "Choose Cover" } -]; - -const CreateList = () => { - const [{ step, error, listId, isWaiting }, setListData] = useState({ - step: 1, - error: null, - listId: null, - isWaiting: false - }); - - const { userInfo } = useUserContext(); - const { updateList } = useListsContext(); - - const formSubmitHandler = async (e) => { - e.preventDefault(); - setListData((prevState) => ({ - ...prevState, - error: null, - isWaiting: true - })); - - const formData = new FormData(e.target); - const listData = {}; - - for (const [key, value] of formData.entries()) { - listData[key] = value; - } - - listData["iso_3166_1"] = "US"; - listData["iso_639_1"] = "en"; - - if (listData["name"].trim().length === 0) return; - - const { success, response } = await createList({ listData }); - - if (!success) { - setListData((prevState) => ({ - ...prevState, - error: "Error creating the list, please try again later.", - isWaiting: false - })); - } else { - const data = await response.json(); - const res = await fetch( - apiEndpoints.lists.getListDetails({ id: data?.id }), - fetchOptions({ - token: userInfo.accessToken - }) - ); - - const listDetails = await res.json(); - updateList((prev) => [listDetails, ...prev]); - setListData({ error: null, listId: data?.id, step: 2, isWaiting: false }); - } - }; - - return ( - -

Create List

- -
-
- {steps.map(({ key, title }, index) => ( -
-
-

{title}

- {index !== steps?.length - 1 ? ( - - ) : null} -
-
- ))} -
- - - {step === 1 ? ( - - {/* create list error */} - {error ? ( -
{error || "error"}
- ) : null} - - - - -
- ) : null} - - {step === 2 ? ( - - setListData((prev) => ({ ...prev, step: 3 }))} - /> - - ) : null} - - {step === 3 ? ( - - - - ) : null} -
-
-
- ); -}; - -export default CreateList; diff --git a/src/components/List/CreateListForm.js b/src/components/List/CreateListForm.js deleted file mode 100644 index 50917cf2..00000000 --- a/src/components/List/CreateListForm.js +++ /dev/null @@ -1,105 +0,0 @@ -import Loading from "components/Loading"; -import { sortOptions } from "globals/constants"; -import { Fragment } from "react"; -import useGetListDetails from "./useGetListDetails"; - -const CreateListForm = ({ submitHandler, id, children }) => { - const { error, listDetails: storedValues, loading } = useGetListDetails({ id }); - - const { listOptions } = sortOptions; - - return ( - - {error ? ( -
- Something went wrong. Please try again later. -
- ) : ( - - {loading ? ( - - ) : ( -
-
- - -
- -
- -