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) => (