diff --git a/.env.example b/.env.example index f30c35507..f206fb47a 100644 --- a/.env.example +++ b/.env.example @@ -1,30 +1,44 @@ -# Backend Server URL +# Backend Server URL [NOT OPTIONAL] # Description: Base URL for the primary backend server used for API requests to fetch anime data, metadata, and other related information. -# Example: VITE_BACKEND_URL="https://api.consumet.org/" +# Example: VITE_BACKEND_URL="https://public-miruro-consumet-api.vercel.app/" VITE_BACKEND_URL="https://public-miruro-consumet-api.vercel.app/" -# Episode Skip Times URL +# Episode Skip Times URL [OPTIONAL] # Description: URL for fetching episode skip times to help users skip intros, outros, and recaps. # Example & Current Setting: VITE_SKIP_TIMES="https://api.aniskip.com/" VITE_SKIP_TIMES="https://api.aniskip.com/" -# Proxy Server URL +# Proxy Server URL [OPTIONAL] # Description: URL of the proxy server used to circumvent CORS issues or when making requests to external services that do not support CORS. # Example: VITE_PROXY_URL="https://corsproxy.io/" VITE_PROXY_URL="https://corsproxy.io/" -# Server Port +# API Key [OPTIONAL] +# Description: The API key used for authenticating requests to secured endpoints. This key ensures that only authorized users and applications can access specific API functions. +# To generate a secure API key, you can use the command: openssl rand -base64 32 +# Example: VITE_API_KEY="your_secret_api_key_here" +VITE_API_KEY="" + +# Server Port [OPTIONAL] # Description: The port number on which the server will listen. Important for server configuration in both development and production environments. # Example & Default: PORT=5173 VITE_PORT=5173 +# Google Analytics [OPTIONAL] +# Description: Google Analytics Measurement ID used for tracking website analytics and user interactions. +# This ID is provided by Google Analytics and is used to initialize the analytics library in the frontend application. +VITE_GA_MEASUREMENT_ID="" -# The following Variables are Optional. - +# AniList Configuration [OPTIONAL] +# Description: Configuration for connecting to AniList API. +# Example & Current Setting: VITE_DEPLOY_PLATFORM="VERCEL" VITE_DEPLOY_PLATFORM="VERCEL" -VITE_CLIENT_ID= +# Description: AniList client ID for authentication. +VITE_CLIENT_ID="" +# Description: AniList client secret for authentication. VITE_CLIENT_SECRET="" -VITE_REDIRECT_URI="" \ No newline at end of file +# Description: Redirect URI for authentication flow with AniList. +VITE_REDIRECT_URI="" diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index 1faec040a..dc12012f6 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -61,7 +61,9 @@ body: label: Which version was acting up? description: Pick the app version that was giving you grief. options: - - 0.4.4 + - 0.5.2 + - 0.5.1 + - 0.5.0 - 0.4.3 - 0.4.2 - 0.4.1 diff --git a/.gitignore b/.gitignore index a7b97b592..db5157f16 100644 --- a/.gitignore +++ b/.gitignore @@ -1,40 +1,32 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - # Dependency directories node_modules/ jspm_packages/ +.pnpm-store/ -# Build outputs +# Vite and Build outputs dist/ dist-ssr/ build/ -temp/ out/ -*.local +.temp/ + +# Bun +.bun/ + +# TypeScript cache +*.tsbuildinfo + +# Compiled binary addons (node-gyp) +build/Release/ # Editor directories and files -.vscode/* -!.vscode/extensions.json .idea/ +.vscode/ *.sublime-workspace *.sublime-project -*.sw? -*.swo -*.swn -*.swp -*.bak -*.orig # Operating System generated files .DS_Store -.DS_Store? ._* .Spotlight-V100 .Trashes @@ -45,98 +37,32 @@ Thumbs.db package-lock.json yarn.lock pnpm-lock.yaml +bun.lockb -# Compiled binary addons (node-gyp) -build/Release/ - -# dotenv environment variables file +# dotenv environment variables files .env .env.local .env.development.local .env.test.local .env.production.local -# PyCharm -*.pyc - -# JetBrains IDEs (IntelliJ, PyCharm, etc) -*.iml -*.ipr -*.iws -.idea/ - -# Visual Studio Code -.vscode/* -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/settings.json - -# Visual Studio -*.suo -*.ntvs* -*.njsproj -*.sln - -# Eclipse -.classpath -.project -.settings/ - -# Vim swap files -*.sw? - -# Backup files -*.bak - -# Tern project files -.tern-project - -# Temporary files -*~ -*.tmp - -# TypeScript cache -*.tsbuildinfo - -# Yarn Integrity file -.yarn-integrity - -# Next.js build output -.next/ - -# Nuxt.js build output -.nuxt/ - -# VuePress build output -.vuepress/dist/ - -# Serverless directories -.serverless/ - -# FuseBox cache -.fusebox/ - -# DynamoDB Local files -.dynamodb/ - -# TernJS port file -.tern-port - -# Storybook build outputs -storybook-static/ - -# Parcel cache +# Caches and logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* .cache/ - -# ESLint cache .eslintcache - -# Stylelint cache .stylelintcache -# Temporary folders -tmp/ -temp/ - -#bun.lockb -bun.lockb \ No newline at end of file +# Temporary files +*.tmp +*~ +*.bak +*.sw? +*.swo +*.swn +*.swp +*.orig diff --git a/api/exchange-token.ts b/api/exchange-token.ts index 54232aab6..74e88b154 100644 --- a/api/exchange-token.ts +++ b/api/exchange-token.ts @@ -1,7 +1,7 @@ import axios from 'axios'; import type { VercelRequest, VercelResponse } from '@vercel/node'; -export default async (req: VercelRequest, res: VercelResponse) => { +async (req: VercelRequest, res: VercelResponse) => { if (req.method !== 'POST') { res.status(405).send('Method Not Allowed'); return; @@ -35,10 +35,23 @@ export default async (req: VercelRequest, res: VercelResponse) => { } else { throw new Error('Access token not found in the response'); } - } catch (error) { - res.status(500).json({ - error: 'Failed to exchange token', - details: error.response?.data || error.message, - }); + } catch (error: unknown) { + // First, check if it's an instance of Error + if (error instanceof Error) { + // Now you can safely read the message property + const message = error.message; + // If it's an axios error, it may have a response object + const details = axios.isAxiosError(error) && error.response ? error.response.data : message; + res.status(500).json({ + error: 'Failed to exchange token', + details, + }); + } else { + // If it's not an Error object, handle it as a generic error + res.status(500).json({ + error: 'Failed to exchange token', + details: 'An unknown error occurred', + }); + } } -}; +} diff --git a/package.json b/package.json index ec2b92829..e6b7d37f2 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "miruro_no_kuon", "private": true, - "version": "0.4.3", + "version": "0.5.2", "type": "module", "scripts": { "dev": "vite --host", diff --git a/server/server.ts b/server/server.ts index a3ae186fc..f9249fa4e 100644 --- a/server/server.ts +++ b/server/server.ts @@ -102,5 +102,7 @@ function getLocalIpAddress() { // Starting the server app.listen(PORT, () => { const ipAddress = getLocalIpAddress(); - console.log(`Server is running at:\n- Localhost: http://localhost:${PORT}\n- Local IP: http://${ipAddress}:${PORT}`); + console.log( + `Server is running at:\n- Localhost: http://localhost:${PORT}\n- Local IP: http://${ipAddress}:${PORT}`, + ); }); diff --git a/src/App.tsx b/src/App.tsx index 3650e2265..2cb0d5549 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -36,7 +36,9 @@ function App() { const measurementId = import.meta.env.VITE_GA_MEASUREMENT_ID; useEffect(() => { - ReactGA.initialize(measurementId); + if (measurementId) { + ReactGA.initialize(measurementId); + } }, [measurementId]); return ( diff --git a/src/client/authService.ts b/src/client/authService.ts index c4fc62b4a..88b7d311d 100644 --- a/src/client/authService.ts +++ b/src/client/authService.ts @@ -114,12 +114,14 @@ const GET_USER_ANIME_LIST = gql` entries { media { id + format title { romaji english } coverImage { large + color } status episodes diff --git a/src/components/Cards/CardItem.tsx b/src/components/Cards/CardItem.tsx index 436c5788a..5a3613054 100644 --- a/src/components/Cards/CardItem.tsx +++ b/src/components/Cards/CardItem.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components'; import { Link } from 'react-router-dom'; import { SkeletonCard, StatusIndicator, type Anime } from '../../index'; // Adjust the import path to correctly point to your index.ts location import { FaPlay } from 'react-icons/fa'; // For the play icon -import { TbCardsFilled } from 'react-icons/tb'; +import { TbCards } from 'react-icons/tb'; import { FaStar, FaCalendarAlt } from 'react-icons/fa'; const StyledCardWrapper = styled(Link)` @@ -257,7 +257,7 @@ export const CardItem: React.FC<{ anime: Anime }> = ({ anime }) => { )} {(anime.totalEpisodes || anime.episodes) && ( <> - + {anime.totalEpisodes || anime.episodes} )} diff --git a/src/components/Home/HomeCarousel.tsx b/src/components/Home/HomeCarousel.tsx index 930028e3f..27bad29b1 100644 --- a/src/components/Home/HomeCarousel.tsx +++ b/src/components/Home/HomeCarousel.tsx @@ -5,7 +5,7 @@ import { Swiper, SwiperSlide } from 'swiper/react'; import 'swiper/swiper-bundle.css'; import { useNavigate } from 'react-router-dom'; import { SkeletonSlide, Anime } from '../../index'; -import { TbCardsFilled } from 'react-icons/tb'; +import { TbCards } from 'react-icons/tb'; import { FaStar } from 'react-icons/fa'; import { FaClock } from 'react-icons/fa6'; @@ -311,7 +311,7 @@ export const HomeCarousel: FC = ({ {type && {type}} {totalEpisodes && ( - + {totalEpisodes} )} diff --git a/src/components/Home/HomeSideBar.tsx b/src/components/Home/HomeSideBar.tsx index be2613382..301911654 100644 --- a/src/components/Home/HomeSideBar.tsx +++ b/src/components/Home/HomeSideBar.tsx @@ -1,11 +1,10 @@ import { useEffect, useState } from 'react'; import styled from 'styled-components'; import { Link } from 'react-router-dom'; // Assuming you're using React Router for navigation -import { TbCardsFilled } from 'react-icons/tb'; +import { TbCards } from 'react-icons/tb'; import { FaStar, FaCalendarAlt } from 'react-icons/fa'; import { Anime, StatusIndicator } from '../../index'; - const SidebarStyled = styled.div` transition: 0.2s ease-in-out; margin: 0; @@ -142,7 +141,7 @@ export const HomeSideBar: React.FC<{ animeData: Anime[] }> = ({ anime.totalEpisodes !== 0 && anime.totalEpisodes !== 0 && ( <> - {anime.currentEpisode} + {anime.currentEpisode} {' / '} {anime.totalEpisodes} diff --git a/src/components/Navigation/DropSearch.tsx b/src/components/Navigation/DropSearch.tsx index 422ca543e..421ef5981 100755 --- a/src/components/Navigation/DropSearch.tsx +++ b/src/components/Navigation/DropSearch.tsx @@ -3,7 +3,7 @@ import styled from 'styled-components'; import { useNavigate } from 'react-router-dom'; import { Anime } from '../../index'; import { FaArrowRight, FaStar } from 'react-icons/fa'; -import { TbCardsFilled } from 'react-icons/tb'; +import { TbCards } from 'react-icons/tb'; import { BsArrowUpSquare, BsArrowDownSquare } from 'react-icons/bs'; import { PiKeyReturn } from 'react-icons/pi'; @@ -224,7 +224,7 @@ export const DropDownSearch: React.FC = ({
 {result.type}    - +   {result.totalEpisodes || 'N/A'}  diff --git a/src/components/Profile/Settings.tsx b/src/components/Profile/Settings.tsx index 9ee572047..8ffed406c 100644 --- a/src/components/Profile/Settings.tsx +++ b/src/components/Profile/Settings.tsx @@ -2,7 +2,7 @@ import React, { useState, useEffect } from 'react'; import styled from 'styled-components'; import { useNavigate } from 'react-router-dom'; import { IoArrowBack } from 'react-icons/io5'; -import { useSettings } from '../../index'; +import { useSettings } from '../../index'; interface Preferences { defaultLanguage: string; titleLanguage: string; @@ -101,7 +101,7 @@ const StyledSelect = styled.select` export const Settings: React.FC = () => { const navigate = useNavigate(); const { settings, setSettings } = useSettings(); - + const [preferences, setPreferences] = useState({ defaultLanguage: settings.defaultLanguage, titleLanguage: 'Romaji', @@ -123,7 +123,7 @@ export const Settings: React.FC = () => { defaultLanguage: settings.defaultLanguage, autoskipIntroOutro: settings.autoSkip ? 'Enabled' : 'Disabled', autoPlay: settings.autoPlay ? 'Enabled' : 'Disabled', - autoNext: settings.autoNext ? 'Enabled' : 'Disabled' + autoNext: settings.autoNext ? 'Enabled' : 'Disabled', })); }, [settings]); @@ -154,10 +154,13 @@ export const Settings: React.FC = () => { } }; - const handlePreferenceChange = (preferenceName: keyof Preferences, value: string) => { - setPreferences(prev => ({ + const handlePreferenceChange = ( + preferenceName: keyof Preferences, + value: string, + ) => { + setPreferences((prev) => ({ ...prev, - [preferenceName]: value + [preferenceName]: value, })); switch (preferenceName) { @@ -178,7 +181,7 @@ export const Settings: React.FC = () => { break; } }; - + const formatPreferenceName = (key: string) => { return key .replace(/([A-Z])/g, ' $1') @@ -222,9 +225,14 @@ export const Settings: React.FC = () => { ) : ( handlePreferenceChange(key as keyof Preferences, e.target.value)} - > + value={preferences[key as keyof Preferences]} + onChange={(e) => + handlePreferenceChange( + key as keyof Preferences, + e.target.value, + ) + } + > {getOptionsForPreference(key).map((option) => (