diff --git a/src/App.tsx b/src/App.tsx index 62c53ad0..eda2c98a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -31,7 +31,7 @@ function App() { path='/watch/:animeId/:animeTitle/:episodeNumber' element={} /> - } /> + } /> } /> } /> } /> diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index bcd49764..c888511a 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -431,11 +431,11 @@ const Navbar = () => { return () => window.removeEventListener('resize', handleResize); }, []); - //navigate to preferences - const navigateToPreferences = () => { - // Check if the current location's pathname is not '/preferences' before navigating - if (location.pathname !== '/preferences') { - navigate('/preferences'); + //navigate to profile + const navigateToProfile = () => { + // Check if the current location's pathname is not '/profile' before navigating + if (location.pathname !== '/profile') { + navigate('/profile'); } }; @@ -510,7 +510,7 @@ const Navbar = () => { {isDarkMode ? : } - {/* + {/* */} diff --git a/src/components/Watch/VideSourceSelector.tsx b/src/components/Watch/VideSourceSelector.tsx index 27e1482e..63b9097f 100644 --- a/src/components/Watch/VideSourceSelector.tsx +++ b/src/components/Watch/VideSourceSelector.tsx @@ -1,6 +1,6 @@ import React from 'react'; import styled from 'styled-components'; -import { FaBell } from 'react-icons/fa'; +import { FaBell, FaDownload } from 'react-icons/fa'; // Props interface interface VideoSourceSelectorProps { @@ -69,6 +69,26 @@ const Button = styled(ButtonBase)` } `; +const DownloadLink = styled.a` + display: inline-block; // Ensures it can be styled like a button + padding: 0.25rem; + padding-left: 0rem; + font-size: 0.9rem; + border: none; + font-weight: bold; + border-radius: var(--global-border-radius); + cursor: pointer; + background-color: var(--global-div); + color: var(--global-text); + text-align: center; + text-decoration: none; // Removes underline from links + margin-left: 0.5rem; + &:hover { + background-color: var(--primary-accent); + transform: scale(1.05); + } +`; + const ResponsiveTableContainer = styled.div` background-color: var(--global-div-tr); padding: 0.6rem; @@ -81,7 +101,6 @@ const ResponsiveTableContainer = styled.div` const EpisodeInfoColumn = styled.div` flex-grow: 1; display: block; - align-text: center; background-color: var(--global-div-tr); border-radius: var(--global-border-radius); padding: 0.6rem; @@ -101,6 +120,7 @@ const EpisodeInfoColumn = styled.div` h4 { margin: 0rem; font-size: 1.15rem; + margin-bottom: 1rem; } @media (max-width: 500px) { p { @@ -108,8 +128,8 @@ const EpisodeInfoColumn = styled.div` margin: 0rem; } h4 { - margin: 0rem; font-size: 1rem; + margin-bottom: 0rem; } } `; @@ -129,6 +149,7 @@ const VideoSourceSelector: React.FC = ({ setSourceType, language, setLanguage, + downloadLink, episodeId, airingTime, nextEpisodenumber, @@ -139,10 +160,16 @@ const VideoSourceSelector: React.FC = ({ {episodeId ? ( <>

- You're watching Episode {episodeId}.{' '} + You're watching Episode {episodeId} + + + Download +

-
-

If current servers don't work, please try other servers.

) : ( @@ -151,7 +178,7 @@ const VideoSourceSelector: React.FC = ({ {airingTime && ( <>

- The next episode ({nextEpisodenumber}) will air in + The next episode, {nextEpisodenumber} will air in {airingTime}.

diff --git a/src/components/Watch/Video/Player.tsx b/src/components/Watch/Video/Player.tsx index abf7a0c8..9c61c478 100644 --- a/src/components/Watch/Video/Player.tsx +++ b/src/components/Watch/Video/Player.tsx @@ -25,6 +25,7 @@ type PlayerProps = { episodeId: string; banner?: string; malId?: string; + updateDownloadLink: (link: string) => void; }; // Define a type for the source object structure within your response @@ -33,7 +34,12 @@ type StreamingSource = { quality: string; }; -export function Player({ episodeId, banner, malId }: PlayerProps) { +export function Player({ + episodeId, + banner, + malId, + updateDownloadLink, +}: PlayerProps) { const player = useRef(null); const [src, setSrc] = useState(''); const [vttUrl, setVttUrl] = useState(''); @@ -67,6 +73,8 @@ export function Player({ episodeId, banner, malId }: PlayerProps) { ); if (backupSource) { setSrc(backupSource.url); + // Assuming `response.download` exists and contains the URL + updateDownloadLink(response.download); // Update parent component's state } else { console.error('Backup source not found'); } @@ -104,7 +112,7 @@ export function Player({ episodeId, banner, malId }: PlayerProps) { URL.revokeObjectURL(vttUrl); // Clean up the Blob URL when the component unmounts } }; - }, [episodeId, malId]); + }, [episodeId, malId, updateDownloadLink]); useEffect(() => { // Set the current time of the player when it's ready @@ -215,18 +223,13 @@ export function Player({ episodeId, banner, malId }: PlayerProps) { aspectRatio='16/9' load='eager' posterLoad='eager' - streamType="on-demand" - storage="storage-key" + streamType='on-demand' + storage='storage-key' > {vttUrl && ( - + )} diff --git a/src/pages/Home.tsx b/src/pages/Home.tsx index eb1321ec..fa554035 100644 --- a/src/pages/Home.tsx +++ b/src/pages/Home.tsx @@ -151,8 +151,8 @@ const Home = () => { useEffect(() => { const desiredItemCount = itemsCount; - // Increase initial fetch count by 20% to account for filtering - const fetchCount = Math.ceil(itemsCount * 1.2); + // Increase initial fetch count by 40% to account for filtering + const fetchCount = Math.ceil(itemsCount * 1.4); const fetchData = async () => { try { diff --git a/src/pages/Profile.tsx b/src/pages/Profile.tsx index 6056ac95..72b9ae1d 100644 --- a/src/pages/Profile.tsx +++ b/src/pages/Profile.tsx @@ -7,7 +7,7 @@ const PreferencesContainer = styled.div` margin-top: 2rem; margin-bottom: 0rem; display: block; - background-color: var(--global-div); + background-color: var(--global-div-tr); border-radius: var(--global-border-radius); padding: 1rem; padding-top: 0.25rem; @@ -15,29 +15,29 @@ const PreferencesContainer = styled.div` align-items: center; justify-content: center; max-width: 40rem; + font-size: 1rem; @media (max-width: 500px) { margin-top: 1rem; margin-bottom: 0rem; - margin-right: 1rem; - margin-left: 1rem; + font-size: 0.9rem; } h2 { align-items: left; - align-text: left; + text-align: left; } `; const StyledButton = styled.button<{ isSelected: boolean }>` background: ${({ isSelected }) => - isSelected ? 'var(--primary-accent)' : 'transparent'}; + isSelected ? 'var(--primary-accent)' : 'var(--global-div)'}; margin-right: 0.5rem; color: var(--global-text); - font-size: 1rem; cursor: pointer; padding: 0.3rem 0.6rem; border-radius: var(--global-border-radius); transition: background-color 0.3s; border: none; + font-size: 1rem; &:hover { background-color: ${({ isSelected }) => isSelected ? 'var(--primary-accent)' : 'var(--primary-accent)'}; @@ -54,6 +54,12 @@ const StyledButton = styled.button<{ isSelected: boolean }>` padding-bottom: 0rem; } @media (max-width: 500px) { + font-size: 0.9rem; + + svg { + font-size: 1rem; + padding-bottom: 0rem; + } display: flex; margin: ${({ isInputToggle }) => (isInputToggle ? '0' : '0')}; } @@ -104,16 +110,44 @@ const StyledDropdown: React.FC = ({ ); -const Profile: React.FC = () => { +const getInitialThemePreference = () => { + const storedThemePreference = localStorage.getItem('themePreference'); + if (storedThemePreference) { + return storedThemePreference === 'dark'; + } else { + // Check system theme when no preference is stored in localStorage + return ( + window.matchMedia && + window.matchMedia('(prefers-color-scheme: dark)').matches + ); + } +}; + +const saveThemePreference = (isDarkMode: any) => { + localStorage.setItem('themePreference', isDarkMode ? 'dark' : 'light'); +}; + +const Profile = () => { + // Other useState hooks remain unchanged... + const [isDarkMode, setIsDarkMode] = useState(getInitialThemePreference()); + useEffect(() => { - const previousTitle = document.title; - document.title = 'Profile | Miruro'; - return () => { - document.title = previousTitle; - }; - }, []); - - const [theme, setTheme] = useState(''); + // Apply the theme based on the isDarkMode state + document.documentElement.classList.toggle('dark-mode', isDarkMode); + // Save the theme preference to localStorage whenever it changes + saveThemePreference(isDarkMode); + }, [isDarkMode]); + + // Function to set the theme to dark mode + const setDarkTheme = () => { + setIsDarkMode(true); + }; + + // Function to set the theme to light mode + const setLightTheme = () => { + setIsDarkMode(false); + }; + const [defaultLanguage, setDefaultLanguage] = useState('Sub'); const [autoskipIntroOutro, setAutoskipIntroOutro] = useState('Disabled'); const [autoPlay, setAutoPlay] = useState('Disabled'); @@ -124,7 +158,8 @@ const Profile: React.FC = () => { return (

- Profile Save + Profile Settings + {/* Save */}

@@ -132,15 +167,15 @@ const Profile: React.FC = () => { Theme setTheme('Light')} + isSelected={!isDarkMode} + onClick={setLightTheme} className='svg' > setTheme('Dark')} + isSelected={isDarkMode} + onClick={setDarkTheme} className='svg' > diff --git a/src/pages/Watch.tsx b/src/pages/Watch.tsx index 752f1378..e45fe4df 100644 --- a/src/pages/Watch.tsx +++ b/src/pages/Watch.tsx @@ -641,6 +641,9 @@ const Watch: React.FC = () => { isMounted = false; }; }, [animeId]); // Dependency array to re-run the effect when animeId changes + const updateDownloadLink = useCallback((link: string) => { + setDownloadLink(link); + }, []); return ( @@ -692,6 +695,7 @@ const Watch: React.FC = () => { episodeId={currentEpisode.id} malId={animeInfo?.malId} banner={selectedBackgroundImage} + updateDownloadLink={updateDownloadLink} /> ) : (