refactor: refactor code a bit

make a hook to abstarct logic from page
changes to .prettierrc
changes to a couple of imports/exports
bunch of screen reader tweaks
This commit is contained in:
zyachel 2022-12-31 22:02:24 +05:30
parent 64f3896258
commit 57b050f196
18 changed files with 226 additions and 208 deletions

View File

@ -1,6 +1,8 @@
{ {
"trailingComma": "es5", "trailingComma": "es5",
"tabWidth": 2, "tabWidth": 2,
"semi": false, "arrowParens": "avoid",
"singleQuote": true "semi": true,
"singleQuote": true,
"jsxSingleQuote": true
} }

View File

@ -17,9 +17,9 @@ const ThemeToggler = (props: Props) => {
return ( return (
<button <button
className={`${styles.button} ${props.className}`} className={`${styles.button} ${props.className}`}
aria-label='Change theme'
onClick={clickHandler} onClick={clickHandler}
> >
<span className='visually-hidden'>Change theme</span>
<svg <svg
className={`icon ${styles.icon}`} className={`icon ${styles.icon}`}
focusable='false' focusable='false'

View File

@ -1,6 +1,6 @@
import Link from 'next/link'; import Link from 'next/link';
import Layout from '../../layouts/Layout'; import Layout from '../../layouts/Layout';
import Meta from '../Meta/Meta'; import Meta from '../meta/Meta';
import styles from '../../styles/modules/components/error/error-info.module.scss'; import styles from '../../styles/modules/components/error/error-info.module.scss';
@ -13,22 +13,22 @@ const ErrorInfo = ({ message = 'Not found, sorry.', statusCode = 404 }) => {
<> <>
<Meta <Meta
title={`${message} (${statusCode})`} title={`${message} (${statusCode})`}
description='you encountered an error page!' description="you encountered an error page!"
/> />
<Layout className={styles.error}> <Layout className={styles.error}>
<svg <svg
className={styles.gnu} className={styles.gnu}
focusable='false' focusable="false"
role='img' role="img"
aria-labelledby='gnu-title gnu-desc' aria-labelledby="gnu-title gnu-desc"
> >
<title id='gnu-title'>GNU and Tux</title> <title id="gnu-title">GNU and Tux</title>
<desc id='gnu-desc'> <desc id="gnu-desc">
A pencil drawing of a big gnu and a small penguin, both very sad. A pencil drawing of a big gnu and a small penguin, both very sad.
GNU is despondently sitting on a bench, and Tux stands beside him, GNU is despondently sitting on a bench, and Tux stands beside him,
looking down and patting him on the back. looking down and patting him on the back.
</desc> </desc>
<use href='/svg/sadgnu.svg#sad-gnu'></use> <use href="/svg/sadgnu.svg#sad-gnu"></use>
</svg> </svg>
<h1 className={`heading heading__primary ${styles.heading}`}> <h1 className={`heading heading__primary ${styles.heading}`}>
<span>{message}</span> <span>{message}</span>
@ -36,8 +36,8 @@ const ErrorInfo = ({ message = 'Not found, sorry.', statusCode = 404 }) => {
</h1> </h1>
<p className={styles.back}> <p className={styles.back}>
Go back to{' '} Go back to{' '}
<Link href='/about'> <Link href="/about">
<a className='link'>the homepage</a> <a className="link">the homepage</a>
</Link> </Link>
. .
</p> </p>

View File

@ -1,27 +1,27 @@
import { Fragment } from 'react' import { Fragment } from 'react';
import Image from 'next/future/image' import Image from 'next/future/image';
import Link from 'next/link' import Link from 'next/link';
import { import {
formatNumber, formatNumber,
formatTime, formatTime,
getProxiedIMDbImgUrl, getProxiedIMDbImgUrl,
modifyIMDbImg, modifyIMDbImg,
} from '../../utils/helpers' } from '../../utils/helpers';
import { Basic } from '../../interfaces/shared/title' import { Basic } from '../../interfaces/shared/title';
import styles from '../../styles/modules/components/title/basic.module.scss' import styles from '../../styles/modules/components/title/basic.module.scss';
type Props = { type Props = {
className: string className: string;
data: Basic data: Basic;
} };
const Basic = ({ data, className }: Props) => { const Basic = ({ data, className }: Props) => {
const titleType = data.type.id const titleType = data.type.id;
const releaseTime = const releaseTime =
titleType === 'tvSeries' titleType === 'tvSeries'
? `${data.releaseYear?.start}-${data.releaseYear?.end || 'present'}` ? `${data.releaseYear?.start}-${data.releaseYear?.end || 'present'}`
: data.releaseYear?.start : data.releaseYear?.start;
return ( return (
<section <section
@ -45,11 +45,11 @@ const Basic = ({ data, className }: Props) => {
alt={data.poster.caption} alt={data.poster.caption}
priority priority
fill fill
sizes="300px" sizes='300px'
/> />
) : ( ) : (
<svg className={styles.image__NA}> <svg className={styles.image__NA}>
<use href="/svg/sprite.svg#icon-image-slash" /> <use href='/svg/sprite.svg#icon-image-slash' />
</svg> </svg>
)} )}
</div> </div>
@ -57,7 +57,7 @@ const Basic = ({ data, className }: Props) => {
<h1 className={`${styles.title} heading heading__primary`}> <h1 className={`${styles.title} heading heading__primary`}>
{data.title} {data.title}
</h1> </h1>
<ul className={styles.meta} aria-label="quick facts"> <ul className={styles.meta} aria-label='quick facts'>
{data.status && data.status.id !== 'released' && ( {data.status && data.status.id !== 'released' && (
<li className={styles.meta__text}>{data.status.text}</li> <li className={styles.meta__text}>{data.status.text}</li>
)} )}
@ -78,7 +78,7 @@ const Basic = ({ data, className }: Props) => {
<p className={styles.rating}> <p className={styles.rating}>
<span className={styles.rating__num}>{data.ratings.avg}</span> <span className={styles.rating__num}>{data.ratings.avg}</span>
<svg className={styles.rating__icon}> <svg className={styles.rating__icon}>
<use href="/svg/sprite.svg#icon-rating"></use> <use href='/svg/sprite.svg#icon-rating'></use>
</svg> </svg>
<span className={styles.rating__text}> Avg. rating</span> <span className={styles.rating__text}> Avg. rating</span>
</p> </p>
@ -87,7 +87,7 @@ const Basic = ({ data, className }: Props) => {
{formatNumber(data.ratings.numVotes)} {formatNumber(data.ratings.numVotes)}
</span> </span>
<svg className={styles.rating__icon}> <svg className={styles.rating__icon}>
<use href="/svg/sprite.svg#icon-like-dislike"></use> <use href='/svg/sprite.svg#icon-like-dislike'></use>
</svg> </svg>
<span className={styles.rating__text}> No. of votes</span> <span className={styles.rating__text}> No. of votes</span>
</p> </p>
@ -99,7 +99,7 @@ const Basic = ({ data, className }: Props) => {
{formatNumber(data.ranking.position)} {formatNumber(data.ranking.position)}
</span> </span>
<svg className={styles.rating__icon}> <svg className={styles.rating__icon}>
<use href="/svg/sprite.svg#icon-graph-rising"></use> <use href='/svg/sprite.svg#icon-graph-rising'></use>
</svg> </svg>
<span className={styles.rating__text}> <span className={styles.rating__text}>
{' '} {' '}
@ -136,7 +136,7 @@ const Basic = ({ data, className }: Props) => {
<span className={styles.overview__text}>{data.plot || '-'}</span> <span className={styles.overview__text}>{data.plot || '-'}</span>
</p> </p>
} }
{data.primaryCrew.map((crewType) => ( {data.primaryCrew.map(crewType => (
<p className={styles.crewType} key={crewType.type.id}> <p className={styles.crewType} key={crewType.type.id}>
<span className={styles.crewType__heading}> <span className={styles.crewType__heading}>
{`${crewType.type.category}: `} {`${crewType.type.category}: `}
@ -153,7 +153,7 @@ const Basic = ({ data, className }: Props) => {
))} ))}
</div> </div>
</section> </section>
) );
} };
export default Basic export default Basic;

View File

@ -1,5 +1,5 @@
import Link from 'next/link'; import Link from 'next/link';
import { NextRouter } from 'next/router'; import { useRouter } from 'next/router';
import { Info } from '../../interfaces/shared/title'; import { Info } from '../../interfaces/shared/title';
import { formatMoney, formatTime } from '../../utils/helpers'; import { formatMoney, formatTime } from '../../utils/helpers';
@ -8,10 +8,10 @@ import styles from '../../styles/modules/components/title/info.module.scss';
type Props = { type Props = {
info: Info; info: Info;
className: string; className: string;
router: NextRouter;
}; };
const Info = ({ info, className, router }: Props) => { const Info = ({ info, className }: Props) => {
const router = useRouter();
const { titleId } = router.query; const { titleId } = router.query;
const { boxOffice, details, meta, keywords, technicalSpecs, accolades } = const { boxOffice, details, meta, keywords, technicalSpecs, accolades } =
info; info;
@ -20,7 +20,7 @@ const Info = ({ info, className, router }: Props) => {
<div className={`${className} ${styles.info}`}> <div className={`${className} ${styles.info}`}>
{meta.infoEpisode && ( {meta.infoEpisode && (
<section className={styles.episodeInfo}> <section className={styles.episodeInfo}>
<h2 className='heading heading__secondary'>Episode info</h2> <h2 className="heading heading__secondary">Episode info</h2>
<div className={styles.episodeInfo__container}> <div className={styles.episodeInfo__container}>
{meta.infoEpisode.numSeason && ( {meta.infoEpisode.numSeason && (
<p className={styles.series}> <p className={styles.series}>
@ -50,14 +50,14 @@ const Info = ({ info, className, router }: Props) => {
{meta.infoEpisode.prevId && ( {meta.infoEpisode.prevId && (
<p> <p>
<Link href={`/title/${meta.infoEpisode.prevId}`}> <Link href={`/title/${meta.infoEpisode.prevId}`}>
<a className='link'>Go to previous episode</a> <a className="link">Go to previous episode</a>
</Link> </Link>
</p> </p>
)} )}
{meta.infoEpisode.nextId && ( {meta.infoEpisode.nextId && (
<p> <p>
<Link href={`/title/${meta.infoEpisode.nextId}`}> <Link href={`/title/${meta.infoEpisode.nextId}`}>
<a className='link'>Go to next episode</a> <a className="link">Go to next episode</a>
</Link> </Link>
</p> </p>
)} )}
@ -66,7 +66,7 @@ const Info = ({ info, className, router }: Props) => {
)} )}
{meta.infoSeries && ( {meta.infoSeries && (
<section className={styles.seriesInfo}> <section className={styles.seriesInfo}>
<h2 className='heading heading__secondary'>Series info</h2> <h2 className="heading heading__secondary">Series info</h2>
<div className={styles.seriesInfo__container}> <div className={styles.seriesInfo__container}>
<p> <p>
<span>Total Seasons: </span> <span>Total Seasons: </span>
@ -82,19 +82,19 @@ const Info = ({ info, className, router }: Props) => {
</p> </p>
<p> <p>
<Link href={`/title/${titleId}/episodes`}> <Link href={`/title/${titleId}/episodes`}>
<a className='link'>See all Episodes</a> <a className="link">See all Episodes</a>
</Link> </Link>
</p> </p>
</div> </div>
</section> </section>
)} )}
<section className={styles.accolades}> <section className={styles.accolades}>
<h2 className='heading heading__secondary'>Accolades</h2> <h2 className="heading heading__secondary">Accolades</h2>
<div className={styles.accolades__container}> <div className={styles.accolades__container}>
{accolades.topRating && ( {accolades.topRating && (
<p> <p>
<Link href={`/chart/top`}> <Link href={`/chart/top`}>
<a className='link'>Top rated (#{accolades.topRating})</a> <a className="link">Top rated (#{accolades.topRating})</a>
</Link> </Link>
</p> </p>
)} )}
@ -112,21 +112,21 @@ const Info = ({ info, className, router }: Props) => {
</p> </p>
<p> <p>
<Link href={`/title/${titleId}/awards`}> <Link href={`/title/${titleId}/awards`}>
<a className='link'>View all awards</a> <a className="link">View all awards</a>
</Link> </Link>
</p> </p>
</div> </div>
</section> </section>
{!!keywords.total && ( {!!keywords.total && (
<section className={styles.keywords}> <section className={styles.keywords}>
<h2 className='heading heading__secondary'>Keywords</h2> <h2 className="heading heading__secondary">Keywords</h2>
<ul className={styles.keywords__container}> <ul className={styles.keywords__container}>
{keywords.list.map(word => ( {keywords.list.map(word => (
<li className={styles.keywords__item} key={word}> <li className={styles.keywords__item} key={word}>
<Link <Link
href={`/search/keyword/?keywords=${word.replace(/\s/g, '-')}`} href={`/search/keyword/?keywords=${word.replace(/\s/g, '-')}`}
> >
<a className='link'>{word}</a> <a className="link">{word}</a>
</Link> </Link>
</li> </li>
))} ))}
@ -135,7 +135,7 @@ const Info = ({ info, className, router }: Props) => {
)} )}
{!!Object.keys(details).length && ( {!!Object.keys(details).length && (
<section className={styles.details}> <section className={styles.details}>
<h2 className='heading heading__secondary'>Details</h2> <h2 className="heading heading__secondary">Details</h2>
<div className={styles.details__container}> <div className={styles.details__container}>
{details.releaseDate && ( {details.releaseDate && (
<p> <p>
@ -156,7 +156,7 @@ const Info = ({ info, className, router }: Props) => {
<Link <Link
href={`/search/title/?country_of_origin=${country.id}`} href={`/search/title/?country_of_origin=${country.id}`}
> >
<a className='link'>{country.text}</a> <a className="link">{country.text}</a>
</Link> </Link>
</span> </span>
))} ))}
@ -168,7 +168,7 @@ const Info = ({ info, className, router }: Props) => {
{details.officialSites.sites.map((site, i) => ( {details.officialSites.sites.map((site, i) => (
<span key={site.url}> <span key={site.url}>
{!!i && ', '} {!!i && ', '}
<a href={site.url} className='link'> <a href={site.url} className="link">
{site.name} {site.name}
</a> </a>
</span> </span>
@ -182,7 +182,7 @@ const Info = ({ info, className, router }: Props) => {
<span key={lang.id}> <span key={lang.id}>
{!!i && ', '} {!!i && ', '}
<Link href={`/search/title/?primary_language=${lang.id}`}> <Link href={`/search/title/?primary_language=${lang.id}`}>
<a className='link'>{lang.text}</a> <a className="link">{lang.text}</a>
</Link> </Link>
</span> </span>
))} ))}
@ -201,7 +201,7 @@ const Info = ({ info, className, router }: Props) => {
<span key={loc}> <span key={loc}>
{!!i && ', '} {!!i && ', '}
<Link href={`/search/title/?locations=${loc}`}> <Link href={`/search/title/?locations=${loc}`}>
<a className='link'>{loc}</a> <a className="link">{loc}</a>
</Link> </Link>
</span> </span>
))} ))}
@ -214,7 +214,7 @@ const Info = ({ info, className, router }: Props) => {
<span key={co.id}> <span key={co.id}>
{!!i && ', '} {!!i && ', '}
<Link href={`/company/${co.id}`}> <Link href={`/company/${co.id}`}>
<a className='link'>{co.name}</a> <a className="link">{co.name}</a>
</Link> </Link>
</span> </span>
))} ))}
@ -225,7 +225,7 @@ const Info = ({ info, className, router }: Props) => {
)} )}
{!!Object.keys(boxOffice).length && ( {!!Object.keys(boxOffice).length && (
<section className={styles.boxoffice}> <section className={styles.boxoffice}>
<h2 className='heading heading__secondary'>Box office</h2> <h2 className="heading heading__secondary">Box office</h2>
<div className={styles.boxoffice__container}> <div className={styles.boxoffice__container}>
{boxOffice.budget && ( {boxOffice.budget && (
<p> <p>
@ -277,7 +277,7 @@ const Info = ({ info, className, router }: Props) => {
)} )}
{!!Object.keys(technicalSpecs).length && ( {!!Object.keys(technicalSpecs).length && (
<section className={styles.technical}> <section className={styles.technical}>
<h2 className='heading heading__secondary'>Technical specs</h2> <h2 className="heading heading__secondary">Technical specs</h2>
<div className={styles.technical__container}> <div className={styles.technical__container}>
{technicalSpecs.runtime && ( {technicalSpecs.runtime && (
<p> <p>
@ -293,7 +293,7 @@ const Info = ({ info, className, router }: Props) => {
<span key={color.id}> <span key={color.id}>
{!!i && ', '} {!!i && ', '}
<Link href={`/search/title/?colors=${color.id}`}> <Link href={`/search/title/?colors=${color.id}`}>
<a className='link'>{color.name}</a> <a className="link">{color.name}</a>
</Link> </Link>
</span> </span>
))} ))}
@ -308,7 +308,7 @@ const Info = ({ info, className, router }: Props) => {
<span key={sound.id}> <span key={sound.id}>
{!!i && ', '} {!!i && ', '}
<Link href={`/search/title/?sound_mixes=${sound.id}`}> <Link href={`/search/title/?sound_mixes=${sound.id}`}>
<a className='link'>{sound.name}</a> <a className="link">{sound.name}</a>
</Link> </Link>
</span> </span>
))} ))}

View File

@ -1,6 +1,5 @@
import Image from 'next/future/image'; import Image from 'next/future/image';
import Link from 'next/link'; import Link from 'next/link';
import { NextRouter } from 'next/router';
import { Media } from '../../interfaces/shared/title'; import { Media } from '../../interfaces/shared/title';
import { getProxiedIMDbImgUrl, modifyIMDbImg } from '../../utils/helpers'; import { getProxiedIMDbImgUrl, modifyIMDbImg } from '../../utils/helpers';
@ -9,21 +8,20 @@ import styles from '../../styles/modules/components/title/media.module.scss';
type Props = { type Props = {
className: string; className: string;
media: Media; media: Media;
router: NextRouter;
}; };
const Media = ({ className, media, router }: Props) => { const Media = ({ className, media }: Props) => {
return ( return (
<div className={`${className} ${styles.media}`}> <div className={`${className} ${styles.media}`}>
{(media.trailer || !!media.videos.total) && ( {(media.trailer || !!media.videos.total) && (
<section className={styles.videos}> <section className={styles.videos}>
<h2 className="heading heading__secondary">Videos</h2> <h2 className='heading heading__secondary'>Videos</h2>
<div className={styles.videos__container}> <div className={styles.videos__container}>
{media.trailer && ( {media.trailer && (
<div key={router.asPath} className={styles.trailer}> <div className={styles.trailer}>
<video <video
aria-label="trailer video" aria-label='trailer video'
// it's a relatively new tag. hence jsx-all1 complains // it's a relatively new tag. hence jsx-all1 complains
aria-description={media.trailer.caption} aria-description={media.trailer.caption}
controls controls
@ -32,7 +30,7 @@ const Media = ({ className, media, router }: Props) => {
modifyIMDbImg(media.trailer.thumbnail) modifyIMDbImg(media.trailer.thumbnail)
)} )}
className={styles.trailer__video} className={styles.trailer__video}
preload="none" preload='none'
> >
{media.trailer.urls.map(source => ( {media.trailer.urls.map(source => (
<source <source
@ -53,9 +51,9 @@ const Media = ({ className, media, router }: Props) => {
<Image <Image
className={styles.video__img} className={styles.video__img}
src={modifyIMDbImg(video.thumbnail)} src={modifyIMDbImg(video.thumbnail)}
alt="" alt=''
fill fill
sizes="400px" sizes='400px'
/> />
<p className={styles.video__caption}> <p className={styles.video__caption}>
{video.caption} ({video.runtime}s) {video.caption} ({video.runtime}s)
@ -68,16 +66,16 @@ const Media = ({ className, media, router }: Props) => {
)} )}
{!!media.images.total && ( {!!media.images.total && (
<section className={styles.images}> <section className={styles.images}>
<h2 className="heading heading__secondary">Images</h2> <h2 className='heading heading__secondary'>Images</h2>
<div className={styles.images__container}> <div className={styles.images__container}>
{media.images.images.map(image => ( {media.images.images.map(image => (
<figure key={image.id} className={styles.image}> <figure key={image.id} className={styles.image}>
<Image <Image
className={styles.image__img} className={styles.image__img}
src={modifyIMDbImg(image.url)} src={modifyIMDbImg(image.url)}
alt="" alt=''
fill fill
sizes="400px" sizes='400px'
/> />
<figcaption className={styles.image__caption}> <figcaption className={styles.image__caption}>
{image.caption.plainText} {image.caption.plainText}

View File

@ -1,4 +1,4 @@
import { NextRouter } from 'next/router'; import { useRouter } from 'next/router';
import Link from 'next/link'; import Link from 'next/link';
import { Reviews } from '../../interfaces/shared/title'; import { Reviews } from '../../interfaces/shared/title';
import { formatNumber } from '../../utils/helpers'; import { formatNumber } from '../../utils/helpers';
@ -6,15 +6,15 @@ import styles from '../../styles/modules/components/title/reviews.module.scss';
type Props = { type Props = {
reviews: Reviews; reviews: Reviews;
router: NextRouter;
}; };
const Reviews = ({ reviews, router }: Props) => { const Reviews = ({ reviews }: Props) => {
const router = useRouter();
const { titleId } = router.query; const { titleId } = router.query;
return ( return (
<section className={styles.reviews}> <section className={styles.reviews}>
<h2 className='heading heading__secondary'>Reviews</h2> <h2 className="heading heading__secondary">Reviews</h2>
{reviews.featuredReview && ( {reviews.featuredReview && (
<article className={styles.reviews__reviewContainer}> <article className={styles.reviews__reviewContainer}>
@ -38,7 +38,7 @@ const Reviews = ({ reviews, router }: Props) => {
{' '} {' '}
by{' '} by{' '}
<Link href={`/user/${reviews.featuredReview.reviewer.id}`}> <Link href={`/user/${reviews.featuredReview.reviewer.id}`}>
<a className='link'>{reviews.featuredReview.reviewer.name}</a> <a className="link">{reviews.featuredReview.reviewer.name}</a>
</Link> </Link>
</span> </span>
<span> on {reviews.featuredReview.date}.</span> <span> on {reviews.featuredReview.date}.</span>
@ -58,21 +58,21 @@ const Reviews = ({ reviews, router }: Props) => {
<div className={styles.reviews__stats}> <div className={styles.reviews__stats}>
<p> <p>
<Link href={`/title/${titleId}/reviews`}> <Link href={`/title/${titleId}/reviews`}>
<a className='link'> <a className="link">
{formatNumber(reviews.numUserReviews)} User reviews {formatNumber(reviews.numUserReviews)} User reviews
</a> </a>
</Link> </Link>
</p> </p>
<p> <p>
<Link href={`/title/${titleId}/externalreviews`}> <Link href={`/title/${titleId}/externalreviews`}>
<a className='link'> <a className="link">
{formatNumber(reviews.numCriticReviews)} Critic reviews {formatNumber(reviews.numCriticReviews)} Critic reviews
</a> </a>
</Link> </Link>
</p> </p>
<p> <p>
<Link href={`/title/${titleId}/criticreviews`}> <Link href={`/title/${titleId}/criticreviews`}>
<a className='link'> {reviews.metacriticScore} Metascore</a> <a className="link"> {reviews.metacriticScore} Metascore</a>
</Link> </Link>
</p> </p>
</div> </div>

View File

@ -0,0 +1,9 @@
import Basic from './Basic';
import Cast from './Cast';
import DidYouKnow from './DidYouKnow';
import Info from './Info';
import Media from './Media';
import MoreLikeThis from './MoreLikeThis';
import Reviews from './Reviews';
export { Basic, Cast, DidYouKnow, Info, Media, MoreLikeThis, Reviews };

View File

@ -0,0 +1,31 @@
import { useCallback, useEffect, useState } from 'react';
import { useRouter } from 'next/router';
/**
* for showing progress bar. could've used nprogress package, but didn't feel like it
* @returns isPageLoading: as the name suggests.
* @returns key: a unique key(in reality, a part of url) telling whether the page has changed or not
*/
const useIsPageLoading = () => {
const router = useRouter();
const [isLoading, setIsLoading] = useState(false);
const handleStart = useCallback(() => setIsLoading(true), []);
const handleEnd = useCallback(() => setIsLoading(false), []);
useEffect(() => {
router.events.on('routeChangeStart', handleStart);
router.events.on('routeChangeComplete', handleEnd);
router.events.on('routeChangeError', handleEnd);
return () => {
router.events.off('routeChangeStart', handleStart);
router.events.off('routeChangeComplete', handleEnd);
router.events.off('routeChangeError', handleEnd);
};
}, [router, handleStart, handleEnd]);
return { isPageLoading: isLoading, key: router.asPath };
};
export default useIsPageLoading;

View File

@ -1,4 +1,4 @@
import ErrorInfo from '../components/Error/ErrorInfo'; import ErrorInfo from '../components/error/ErrorInfo';
const Error404 = () => { const Error404 = () => {
return <ErrorInfo />; return <ErrorInfo />;

View File

@ -1,6 +1,6 @@
import ErrorInfo from '../components/Error/ErrorInfo'; import ErrorInfo from '../components/error/ErrorInfo';
const Error500 = () => { const Error500 = () => {
return <ErrorInfo message='server messed up, sorry.' statusCode={500} />; return <ErrorInfo message="server messed up, sorry." statusCode={500} />;
}; };
export default Error500; export default Error500;

View File

@ -1,38 +1,21 @@
import { useCallback, useEffect, useState } from 'react';
import type { AppProps } from 'next/app'; import type { AppProps } from 'next/app';
import { useRouter } from 'next/router'; import usePageLoading from '../hooks/usePageLoading';
import ProgressBar from '../components/loaders/ProgressBar'; import ProgressBar from '../components/loaders/ProgressBar';
import ThemeProvider from '../context/theme-context'; import ThemeProvider from '../context/theme-context';
import '../styles/main.scss'; import '../styles/main.scss';
import { useRouter } from 'next/router';
const ModifiedApp = ({ Component, pageProps }: AppProps) => { const ModifiedApp = ({ Component, pageProps }: AppProps) => {
// for showing progress bar const { isPageLoading, key } = usePageLoading();
// could've used nprogress package, but didn't feel like it
const router = useRouter();
const [isLoading, setIsLoading] = useState(false);
const handleStart = useCallback(() => setIsLoading(true), []);
const handleEnd = useCallback(() => setIsLoading(false), []);
useEffect(() => {
router.events.on('routeChangeStart', handleStart);
router.events.on('routeChangeComplete', handleEnd);
router.events.on('routeChangeError', handleEnd);
return () => {
router.events.off('routeChangeStart', handleStart);
router.events.off('routeChangeComplete', handleEnd);
router.events.off('routeChangeError', handleEnd);
};
}, [router, handleStart, handleEnd]);
//
return ( return (
<ThemeProvider> <ThemeProvider>
{isLoading && <ProgressBar />} {isPageLoading && <ProgressBar />}
<Component {...pageProps} /> <Component
{...pageProps}
key={key} /* passing key to force react to remound components */
/>
</ThemeProvider> </ThemeProvider>
); );
}; };

View File

@ -3,15 +3,17 @@ import Document, { Html, Head, Main, NextScript } from 'next/document';
// for preventing Flash of inAccurate coloR Theme(fart) // for preventing Flash of inAccurate coloR Theme(fart)
// chris coyier came up with that acronym(https://css-tricks.com/flash-of-inaccurate-color-theme-fart/) // chris coyier came up with that acronym(https://css-tricks.com/flash-of-inaccurate-color-theme-fart/)
const setInitialTheme = ` const setInitialTheme = `
(() => {
document.documentElement.dataset.js = true; document.documentElement.dataset.js = true;
document.documentElement.dataset.theme = (() => { let theme = 'light';
let themeColor = '#ffe5ef';
const userPrefersTheme = window.localStorage.getItem('theme') || null; const userPrefersTheme = window.localStorage.getItem('theme') || null;
const browserPrefersDarkTheme = window.matchMedia( const browserPrefersDarkTheme = window.matchMedia('(prefers-color-scheme: dark)').matches;
'(prefers-color-scheme: dark)' if (userPrefersTheme) theme = userPrefersTheme;
).matches; else if (browserPrefersDarkTheme) theme = 'dark';
if (userPrefersTheme) return userPrefersTheme; if(theme === 'dark') themeColor = '#141c2e';
else if (browserPrefersDarkTheme) return 'dark'; document.documentElement.dataset.theme = theme;
else return 'light'; document.querySelector('meta[name="theme-color"]').setAttribute('content', themeColor);
})(); })();
`; `;

View File

@ -1,19 +1,19 @@
/* eslint-disable react/no-unescaped-entities */ /* eslint-disable react/no-unescaped-entities */
import Link from 'next/link' import Link from 'next/link';
import Meta from '../../components/Meta/Meta' import Meta from '../../components/meta/Meta';
import Layout from '../../layouts/Layout' import Layout from '../../layouts/Layout';
import styles from '../../styles/modules/pages/about/about.module.scss' import styles from '../../styles/modules/pages/about/about.module.scss';
const About = () => { const About = () => {
return ( return (
<> <>
<Meta <Meta
title="About" title='About'
description="libremdb is a free & open source IMDb front-end. It allows you to see information about movies, tv shows, video games without any ads or tracking." description='libremdb is a free & open source IMDb front-end. It allows you to see information about movies, tv shows, video games without any ads or tracking.'
/> />
<Layout full className={styles.about}> <Layout full className={styles.about}>
<section id="features" className={styles.features}> <section id='features' className={styles.features}>
<h2 <h2
className={`heading heading__secondary ${styles.features__heading}`} className={`heading heading__secondary ${styles.features__heading}`}
> >
@ -22,12 +22,12 @@ const About = () => {
<ul className={styles.features__list}> <ul className={styles.features__list}>
<li className={styles.feature}> <li className={styles.feature}>
<svg <svg
aria-hidden="true" aria-hidden='true'
focusable="false" focusable='false'
role="img" role='img'
className={styles.feature__icon} className={styles.feature__icon}
> >
<use href="/svg/sprite.svg#icon-eye-slash"></use> <use href='/svg/sprite.svg#icon-eye-slash'></use>
</svg> </svg>
<h3 <h3
className={`heading heading__tertiary ${styles.feature__heading}`} className={`heading heading__tertiary ${styles.feature__heading}`}
@ -41,12 +41,12 @@ const About = () => {
</li> </li>
<li className={styles.feature}> <li className={styles.feature}>
<svg <svg
aria-hidden="true" aria-hidden='true'
focusable="false" focusable='false'
role="img" role='img'
className={styles.feature__icon} className={styles.feature__icon}
> >
<use href="/svg/sprite.svg#icon-palette"></use> <use href='/svg/sprite.svg#icon-palette'></use>
</svg> </svg>
<h3 <h3
className={`heading heading__tertiary ${styles.feature__heading}`} className={`heading heading__tertiary ${styles.feature__heading}`}
@ -60,12 +60,12 @@ const About = () => {
</li> </li>
<li className={styles.feature}> <li className={styles.feature}>
<svg <svg
aria-hidden="true" aria-hidden='true'
focusable="false" focusable='false'
role="img" role='img'
className={styles.feature__icon} className={styles.feature__icon}
> >
<use href="/svg/sprite.svg#icon-responsive"></use> <use href='/svg/sprite.svg#icon-responsive'></use>
</svg> </svg>
<h3 <h3
className={`heading heading__tertiary ${styles.feature__heading}`} className={`heading heading__tertiary ${styles.feature__heading}`}
@ -79,7 +79,7 @@ const About = () => {
</li> </li>
</ul> </ul>
</section> </section>
<section id="faq" className={styles.faqs}> <section id='faq' className={styles.faqs}>
<h2 className={`heading heading__secondary ${styles.faqs__heading}`}> <h2 className={`heading heading__secondary ${styles.faqs__heading}`}>
Questions you may have Questions you may have
</h2> </h2>
@ -91,21 +91,25 @@ const About = () => {
<p className={styles.faq__description}> <p className={styles.faq__description}>
Replace `imdb.com` in any IMDb URL with any of the instances. Replace `imdb.com` in any IMDb URL with any of the instances.
For example: ` For example: `
<a href="https://imdb.com/title/tt1049413" className="link"> <a
href='https://imdb.com/title/tt1049413'
className='link'
target='_blank'
rel='noreferrer'
>
imdb.com/title/tt1049413 imdb.com/title/tt1049413
</a> </a>
` to ` ` to `
<a <Link href='/title/tt1049413'>
href="https://libremdb.iket.me/title/tt1049413" <a className='link'>
className="link" {process.env.NEXT_PUBLIC_URL || ''}/title/tt1049413
>
libremdb.iket.me/title/tt1049413
</a> </a>
</Link>
` . To avoid changing the URLs manually, you can use extensions ` . To avoid changing the URLs manually, you can use extensions
like{' '} like{' '}
<a <a
href="https://github.com/libredirect/libredirect/" href='https://github.com/libredirect/libredirect/'
className="link" className='link'
> >
LibRedirect LibRedirect
</a> </a>
@ -140,33 +144,23 @@ const About = () => {
instance to avoid exposing your IP address, browser information instance to avoid exposing your IP address, browser information
and other personally identifiable metadata ( and other personally identifiable metadata (
<a <a
href="https://github.com/httpjamesm" href='https://github.com/httpjamesm'
target="_blank" target='_blank'
rel="noopener noreferrer" rel='noopener noreferrer'
className="link" className='link'
> >
Contributor Contributor
</a> </a>
). ).
</p> </p>
</details> </details>
<details className={styles.faq}>
<summary className={styles.faq__summary}>
Will Amazon track me then?
</summary>
<p className={styles.faq__description}>
Also nope. All Amazon will see is the libremdb instance making
the request, not you. IP address, browser information and other
personally identifiable metadata is hidden from Amazon.
</p>
</details>
<details className={styles.faq}> <details className={styles.faq}>
<summary className={styles.faq__summary}> <summary className={styles.faq__summary}>
Why not just use IMDb? Why not just use IMDb?
</summary> </summary>
<p className={styles.faq__description}> <p className={styles.faq__description}>
Refer to the{' '} Refer to the{' '}
<a className="link" href="#features"> <a className='link' href='#features'>
features section features section
</a>{' '} </a>{' '}
above. above.
@ -196,8 +190,8 @@ const About = () => {
</summary> </summary>
<p className={styles.faq__description}> <p className={styles.faq__description}>
That's great! I've a couple of{' '} That's great! I've a couple of{' '}
<Link href="/contact"> <Link href='/contact'>
<a className="link">contact methods</a> <a className='link'>contact methods</a>
</Link> </Link>
. Send your beautiful suggestions(or complaints), or just drop a . Send your beautiful suggestions(or complaints), or just drop a
hi. hi.
@ -207,7 +201,7 @@ const About = () => {
</section> </section>
</Layout> </Layout>
</> </>
) );
} };
export default About export default About;

View File

@ -1,4 +1,4 @@
import Meta from '../../components/Meta/Meta'; import Meta from '../../components/meta/Meta';
import Layout from '../../layouts/Layout'; import Layout from '../../layouts/Layout';
import styles from '../../styles/modules/pages/contact/contact.module.scss'; import styles from '../../styles/modules/pages/contact/contact.module.scss';
@ -7,10 +7,10 @@ const Contact = () => {
return ( return (
<> <>
<Meta <Meta
title='Contact' title="Contact"
description='Contact page of libremdb, a free & open source IMDb front-end.' description="Contact page of libremdb, a free & open source IMDb front-end."
/> />
<Layout className=''> <Layout className="">
<section className={styles.contact}> <section className={styles.contact}>
<h1 className={`heading heading__primary ${styles.contact__heading}`}> <h1 className={`heading heading__primary ${styles.contact__heading}`}>
Contact Contact
@ -19,22 +19,22 @@ const Contact = () => {
<div className={styles.list}> <div className={styles.list}>
<p className={styles.item}> <p className={styles.item}>
You can use{' '} You can use{' '}
<a href='https://github.com/zyachel/libremdb' className='link'> <a href="https://github.com/zyachel/libremdb" className="link">
GitHub GitHub
</a>{' '} </a>{' '}
or{' '} or{' '}
<a href='https://codeberg.org/zyachel/libremdb' className='link'> <a href="https://codeberg.org/zyachel/libremdb" className="link">
Codeberg Codeberg
</a>{' '} </a>{' '}
for general issues, questions, or requests. for general issues, questions, or requests.
</p> </p>
<p className={styles.item}> <p className={styles.item}>
In case you wish to contact me personally, I'm reachable via{' '} In case you wish to contact me personally, I'm reachable via{' '}
<a className='link' href='https://matrix.to/#/@ninal:matrix.org'> <a className="link" href="https://matrix.to/#/@ninal:matrix.org">
[matrix] [matrix]
</a>{' '} </a>{' '}
and{' '} and{' '}
<a className='link' href='mailto:aricla@protonmail.com'> <a className="link" href="mailto:aricla@protonmail.com">
email email
</a> </a>
. .

View File

@ -1,7 +1,7 @@
import Meta from '../../components/Meta/Meta' import Meta from '../../components/meta/Meta';
import Layout from '../../layouts/Layout' import Layout from '../../layouts/Layout';
import styles from '../../styles/modules/pages/privacy/privacy.module.scss' import styles from '../../styles/modules/pages/privacy/privacy.module.scss';
const Privacy = () => { const Privacy = () => {
return ( return (
@ -55,7 +55,7 @@ const Privacy = () => {
</section> </section>
</Layout> </Layout>
</> </>
) );
} };
export default Privacy export default Privacy;

View File

@ -1,35 +1,34 @@
// external // external
import { GetServerSideProps, GetStaticProps, GetStaticPaths } from 'next' import { GetServerSideProps, GetStaticProps, GetStaticPaths } from 'next'
import { useRouter } from 'next/router' import Head from 'next/head';
import { useRouter } from 'next/router';
// local // local
import Meta from '../../../components/Meta/Meta' import Meta from '../../../components/meta/Meta';
import Layout from '../../../layouts/Layout' import Layout from '../../../layouts/Layout';
import title from '../../../utils/fetchers/title' import ErrorInfo from '../../../components/error/ErrorInfo';
// components import {
import ErrorInfo from '../../../components/Error/ErrorInfo' Basic,
import Basic from '../../../components/title/Basic' Cast,
import Media from '../../../components/title/Media' DidYouKnow,
import Cast from '../../../components/title/Cast' Info,
import DidYouKnow from '../../../components/title/DidYouKnow' Media,
import Info from '../../../components/title/Info' MoreLikeThis,
import Reviews from '../../../components/title/Reviews' Reviews,
import MoreLikeThis from '../../../components/title/MoreLikeThis' } from '../../../components/title';
// misc // misc
import Title from '../../../interfaces/shared/title' import Title from '../../../interfaces/shared/title';
import { AppError } from '../../../interfaces/shared/error' import { AppError } from '../../../interfaces/shared/error';
import title from '../../../utils/fetchers/title';
import { getProxiedIMDbImgUrl } from '../../../utils/helpers';
// styles // styles
import styles from '../../../styles/modules/pages/title/title.module.scss' import styles from '../../../styles/modules/pages/title/title.module.scss';
import Head from 'next/head'
import { getProxiedIMDbImgUrl } from '../../../utils/helpers'
type Props = { data: Title; error: null } | { error: AppError; data: null } type Props = { data: Title; error: null } | { error: AppError; data: null };
// TO-DO: make a wrapper page component to display errors, if present in props // TO-DO: make a wrapper page component to display errors, if present in props
const TitleInfo = ({ data, error }: Props) => { const TitleInfo = ({ data, error }: Props) => {
const router = useRouter()
if (error) if (error)
return <ErrorInfo message={error.message} statusCode={error.statusCode} /> return <ErrorInfo message={error.message} statusCode={error.statusCode} />;
const info = { const info = {
meta: data.meta, meta: data.meta,
@ -38,7 +37,7 @@ const TitleInfo = ({ data, error }: Props) => {
boxOffice: data.boxOffice, boxOffice: data.boxOffice,
technicalSpecs: data.technicalSpecs, technicalSpecs: data.technicalSpecs,
accolades: data.accolades, accolades: data.accolades,
} };
return ( return (
<> <>
@ -60,18 +59,18 @@ const TitleInfo = ({ data, error }: Props) => {
</Head> </Head>
<Layout className={styles.title}> <Layout className={styles.title}>
<Basic data={data.basic} className={styles.basic} /> <Basic data={data.basic} className={styles.basic} />
<Media className={styles.media} media={data.media} router={router} /> <Media className={styles.media} media={data.media} />
<Cast className={styles.cast} cast={data.cast} /> <Cast className={styles.cast} cast={data.cast} />
<div className={styles.textarea}> <div className={styles.textarea}>
<DidYouKnow data={data.didYouKnow} /> <DidYouKnow data={data.didYouKnow} />
<Reviews reviews={data.reviews} router={router} /> <Reviews reviews={data.reviews} />
</div> </div>
<Info className={styles.infoarea} info={info} router={router} /> <Info className={styles.infoarea} info={info} />
<MoreLikeThis className={styles.related} data={data.moreLikeThis} /> <MoreLikeThis className={styles.related} data={data.moreLikeThis} />
</Layout> </Layout>
</> </>
) );
} };
// TO-DO: make a getServerSideProps wrapper for handling errors // TO-DO: make a getServerSideProps wrapper for handling errors
export const getServerSideProps: GetServerSideProps = async (ctx) => { export const getServerSideProps: GetServerSideProps = async (ctx) => {