feat: major rewrite

the application is now rewritten in next.js. this commit also adds the ability to see trailers, did you know, more like this, etc. on title page.

BREAKING CHANGE: the whole application is rewritten from scratch.
This commit is contained in:
zyachel
2022-09-11 19:37:24 +05:30
committed by zyachel
parent 620ddf348a
commit 9891204f5a
129 changed files with 6314 additions and 4671 deletions

7
src/pages/404.tsx Normal file
View File

@ -0,0 +1,7 @@
import ErrorInfo from '../components/Error/ErrorInfo';
const Error404 = () => {
return <ErrorInfo />;
};
export default Error404;

6
src/pages/500.tsx Normal file
View File

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

40
src/pages/_app.tsx Normal file
View File

@ -0,0 +1,40 @@
import { useCallback, useEffect, useState } from 'react';
import type { AppProps } from 'next/app';
import { useRouter } from 'next/router';
import ProgressBar from '../components/loaders/ProgressBar';
import ThemeProvider from '../context/theme-context';
import '../styles/main.scss';
const ModifiedApp = ({ Component, pageProps }: AppProps) => {
// for showing progress bar
// 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 (
<ThemeProvider>
{isLoading && <ProgressBar />}
<Component {...pageProps} />
</ThemeProvider>
);
};
export default ModifiedApp;

38
src/pages/_document.tsx Normal file
View File

@ -0,0 +1,38 @@
import Document, { Html, Head, Main, NextScript } from 'next/document';
// 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/)
const setInitialTheme = `
document.documentElement.dataset.js = true;
document.documentElement.dataset.theme = (() => {
const userPrefersTheme = window.localStorage.getItem('theme') || null;
const browserPrefersDarkTheme = window.matchMedia(
'(prefers-color-scheme: dark)'
).matches;
if (userPrefersTheme) return userPrefersTheme;
else if (browserPrefersDarkTheme) return 'dark';
else return 'light';
})();
`;
const ModifiedDocument = class extends Document {
static async getInitialProps(ctx: any) {
const initialProps = await Document.getInitialProps(ctx);
return { ...initialProps };
}
render() {
return (
<Html lang='en'>
<Head />
<body>
<script dangerouslySetInnerHTML={{ __html: setInitialTheme }} />
<Main />
<NextScript />
</body>
</Html>
);
}
};
export default ModifiedDocument;

176
src/pages/about/index.tsx Normal file
View File

@ -0,0 +1,176 @@
/* eslint-disable react/no-unescaped-entities */
import Link from 'next/link';
import Meta from '../../components/Meta/Meta';
import Layout from '../../layouts/Layout';
import styles from '../../styles/modules/pages/about/about.module.scss';
const About = () => {
return (
<>
<Meta
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.'
/>
<Layout full className={styles.about}>
<section id='features' className={styles.features}>
<h2
className={`heading heading__secondary ${styles.features__heading}`}
>
Some features
</h2>
<ul className={styles.features__list}>
<li className={styles.feature}>
<svg
aria-hidden='true'
focusable='false'
role='img'
className={styles.feature__icon}
>
<use href='/svg/sprite.svg#icon-eye-slash'></use>
</svg>
<h3
className={`heading heading__tertiary ${styles.feature__heading}`}
>
No ads or tracking
</h3>
<p className={styles.feature__text}>
Browse any movie info without being tracked or bombarded by
annoying ads.
</p>
</li>
<li className={styles.feature}>
<svg
aria-hidden='true'
focusable='false'
role='img'
className={styles.feature__icon}
>
<use href='/svg/sprite.svg#icon-palette'></use>
</svg>
<h3
className={`heading heading__tertiary ${styles.feature__heading}`}
>
Modern interface
</h3>
<p className={styles.feature__text}>
Modern interface with curated colors supporting both dark and
light themes.
</p>
</li>
<li className={styles.feature}>
<svg
aria-hidden='true'
focusable='false'
role='img'
className={styles.feature__icon}
>
<use href='/svg/sprite.svg#icon-responsive'></use>
</svg>
<h3
className={`heading heading__tertiary ${styles.feature__heading}`}
>
Responsive design
</h3>
<p className={styles.feature__text}>
Be it your small mobile or big computer screen, it's fully
responsive.
</p>
</li>
</ul>
</section>
<section id='faq' className={styles.faqs}>
<h2 className={`heading heading__secondary ${styles.faqs__heading}`}>
Questions you may have
</h2>
<div className={styles.faqs__list}>
<details className={styles.faq}>
<summary className={styles.faq__summary}>Why is it slow?</summary>
<p className={styles.faq__description}>
Whenever you request info about a movie/show on libremdb, 4
trips are made(2 between your browser and libremdb's server, and
2 between libremdb's server and IMDb's server) instead of the
usual 2 trips when you visit a website. For this reason there's
a noticable delay. This is a bit of inconvenience you'll have to
face should you wish to use this website.
</p>
</details>
<details className={styles.faq}>
<summary className={styles.faq__summary}>
It doesn't have all routes.
</summary>
<p className={styles.faq__description}>
I'll implement more with time :)
</p>
</details>
<details className={styles.faq}>
<summary className={styles.faq__summary}>
I see connection being made to some Amazon domains.
</summary>
<p className={styles.faq__description}>
For now, images and videos are directly served from Amazon. If I
have enough time in the future, I'll implement a way to serve
the images from libremdb instead.
</p>
</details>
<details className={styles.faq}>
<summary className={styles.faq__summary}>
Will Amazon track me then?
</summary>
<p className={styles.faq__description}>
They may log your IP address, useragent, and other such
identifiers. I'd recommend using a VPN, or accessing the website
through TOR for mitigating this risk.
</p>
</details>
<details className={styles.faq}>
<summary className={styles.faq__summary}>
Why not just use IMDb?
</summary>
<p className={styles.faq__description}>
Refer to the{' '}
<a className='link' href='#features'>
features section
</a>{' '}
above.
</p>
</details>
<details className={styles.faq}>
<summary className={styles.faq__summary}>
Why didn't you use other databases like TMDB or OMDb?
</summary>
<p className={styles.faq__description}>
IMDb simply has superior dataset compared to all other
alternatives. With that being said, I'd encourage you to check
out those alternatives too.
</p>
</details>
<details className={styles.faq}>
<summary className={styles.faq__summary}>
Your website name is quite, ehm, lame.
</summary>
<p className={styles.faq__description}>
Let's just say I'm not very good at naming things.
</p>
</details>
<details className={styles.faq}>
<summary className={styles.faq__summary}>
I have some ideas/features/suggestions.
</summary>
<p className={styles.faq__description}>
That's great! I've a couple of{' '}
<Link href='/contact'>
<a className='link'>contact methods</a>
</Link>
. Send your beautiful suggestions(or complaints), or just drop a
hi.
</p>
</details>
</div>
</section>
</Layout>
</>
);
};
export default About;

View File

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

View File

@ -0,0 +1,74 @@
import Meta from '../../components/Meta/Meta';
import Layout from '../../layouts/Layout';
import styles from '../../styles/modules/pages/privacy/privacy.module.scss';
const Privacy = () => {
return (
<>
<Meta
title='Privacy'
description='Privacy policy of libremdb, a free & open source IMDb front-end.'
/>
<Layout className={styles.privacy}>
<section className={styles.policy}>
<h1 className={`heading heading__primary ${styles.policy__heading}`}>
Privacy Policy
</h1>
<div className={styles.list}>
<div className={styles.item}>
<h2
className={`heading heading__secondary ${styles.item__heading}`}
>
Information collected
</h2>
<p className={styles.item__text}>No information is collected.</p>
</div>
<div className={styles.item}>
<h2
className={`heading heading__secondary ${styles.item__heading}`}
>
Information stored in your browser
</h2>
<p className={styles.item__text}>
A key named 'theme' is stored in Local Storage provided by your
browser, if you ever override the default theme. To remove it,
go to site data settings, and clear the data for this website.
</p>
<p className={styles.item__text}>
To permamently disable libremdb from storing your theme
prefrences, either turn off JavaScript or disable access to
Local Storage for libremdb.
</p>
</div>
<div className={styles.item}>
<h2
className={`heading heading__secondary ${styles.item__heading}`}
>
Information collected by other services
</h2>
<p className={styles.item__text}>
libremdb connects to 'media-amazon.com' and 'media-imdb.com' for
fetching images and videos. So, Amazon might log your IP
address, and other information(such as http headers) sent by
your browser.
</p>
</div>
</div>
<footer className={styles.metadata}>
<p>
Last updated on <time>10 september, 2022.</time>
</p>
<p>
You can see the full revision history of this privacy policy on
GitHub, or Codeberg.
</p>
</footer>
</section>
</Layout>
</>
);
};
export default Privacy;

View File

@ -0,0 +1,108 @@
// external
import { GetServerSideProps, GetStaticProps, GetStaticPaths } from 'next';
import { useRouter } from 'next/router';
// local
import Meta from '../../../components/Meta/Meta';
import Layout from '../../../layouts/Layout';
import title from '../../../utils/fetchers/title';
// components
import ErrorInfo from '../../../components/Error/ErrorInfo';
import Basic from '../../../components/title/Basic';
import Media from '../../../components/title/Media';
import Cast from '../../../components/title/Cast';
import DidYouKnow from '../../../components/title/DidYouKnow';
import Info from '../../../components/title/Info';
import Reviews from '../../../components/title/Reviews';
import MoreLikeThis from '../../../components/title/MoreLikeThis';
// misc
import Title from '../../../interfaces/shared/title';
import { AppError } from '../../../interfaces/shared/error';
// styles
import styles from '../../../styles/modules/pages/title/title.module.scss';
type Props = { data: Title; error: null } | { error: AppError; data: null };
// TO-DO: make a wrapper page component to display errors, if present in props
const TitleInfo = ({ data, error }: Props) => {
const router = useRouter();
if (error)
return <ErrorInfo message={error.message} statusCode={error.statusCode} />;
const info = {
meta: data.meta,
keywords: data.keywords,
details: data.details,
boxOffice: data.boxOffice,
technicalSpecs: data.technicalSpecs,
accolades: data.accolades,
};
return (
<>
<Meta
title={`${data.basic.title} (${
data.basic.releaseYear?.start || data.basic.type.name
})`}
description={data.basic.plot || undefined}
/>
<Layout className={styles.title}>
<Basic data={data.basic} className={styles.basic} />
<Media className={styles.media} media={data.media} router={router} />
<Cast className={styles.cast} cast={data.cast} />
<div className={styles.textarea}>
<DidYouKnow data={data.didYouKnow} />
<Reviews reviews={data.reviews} router={router} />
</div>
<Info className={styles.infoarea} info={info} router={router} />
<MoreLikeThis className={styles.related} data={data.moreLikeThis} />
</Layout>
</>
);
};
// TO-DO: make a getServerSideProps wrapper for handling errors
export const getServerSideProps: GetServerSideProps = async ctx => {
const titleId = ctx.params!.titleId as string;
try {
const data = await title(titleId);
return { props: { data, error: null } };
} catch (error: any) {
const { message, statusCode } = error;
ctx.res.statusCode = statusCode;
ctx.res.statusMessage = message;
return { props: { error: { message, statusCode }, data: null } };
}
};
export default TitleInfo;
// could've used getStaticProps instead of getServerSideProps, but meh.
/*
export const getStaticProps: GetStaticProps = async ctx => {
const titleId = ctx.params!.titleId as string;
try {
const data = await title(titleId);
return {
props: { data, error: null },
revalidate: 60 * 60 * 24, // 1 day
};
} catch (error) {
// console.log(error);
return { notFound: true };
}
};
export const getStaticPaths: GetStaticPaths = () => {
return {
paths: [{ params: { titleId: 'tt0133093' } }],
fallback: 'blocking',
};
};
*/