mirror of
https://github.com/Sonny93/my-links.git
synced 2025-12-08 22:53:25 +00:00
refactor: change routes to the home page and (new) "app" page
Tldr : "/" becomes "/app" and "/about" becomes "/"
This commit is contained in:
@@ -13,10 +13,7 @@ export default function NavbarUntranslated() {
|
||||
<LinkTag href={PATHS.HOME}>MyLinks</LinkTag>
|
||||
</li>
|
||||
<li>
|
||||
<LinkTag href={PATHS.PRIVACY}>Privacy</LinkTag>
|
||||
</li>
|
||||
<li>
|
||||
<LinkTag href={PATHS.TERMS}>Terms of use</LinkTag>
|
||||
<LinkTag href={PATHS.REPO_GITHUB}>GitHub</LinkTag>
|
||||
</li>
|
||||
{status === 'authenticated' ? (
|
||||
<li>
|
||||
|
||||
@@ -2,6 +2,7 @@ const PATHS = {
|
||||
LOGIN: '/login',
|
||||
LOGOUT: '/logout',
|
||||
HOME: '/',
|
||||
APP: '/app',
|
||||
PRIVACY: '/privacy',
|
||||
TERMS: '/terms',
|
||||
ADMIN: '/admin',
|
||||
|
||||
@@ -16,7 +16,7 @@ export function buildSearchItem(
|
||||
url:
|
||||
type === 'link'
|
||||
? (item as LinkWithCategory).url
|
||||
: `${PATHS.HOME}?categoryId=${item.id}`,
|
||||
: `${PATHS.APP}?categoryId=${item.id}`,
|
||||
type,
|
||||
category: type === 'link' ? (item as LinkWithCategory).category : undefined,
|
||||
};
|
||||
|
||||
@@ -23,8 +23,8 @@ function MyApp({ Component, pageProps: { session, ...pageProps } }) {
|
||||
// TODO: use dynamic locale import
|
||||
dayjs.locale(i18n.language);
|
||||
|
||||
useHotkeys(Keys.CLOSE_SEARCH_KEY, () => router.push(PATHS.HOME), {
|
||||
enabled: router.pathname !== PATHS.HOME,
|
||||
useHotkeys(Keys.CLOSE_SEARCH_KEY, () => router.push(PATHS.APP), {
|
||||
enabled: router.pathname !== PATHS.APP,
|
||||
enableOnFormTags: ['INPUT'],
|
||||
});
|
||||
|
||||
|
||||
@@ -1,104 +0,0 @@
|
||||
import clsx from 'clsx';
|
||||
import Footer from 'components/Footer/Footer';
|
||||
import Navbar from 'components/Navbar/Navbar';
|
||||
import PageTransition from 'components/PageTransition';
|
||||
import { getServerSideTranslation } from 'i18n';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { IconType } from 'react-icons';
|
||||
import { AiFillFolderOpen } from 'react-icons/ai';
|
||||
import { FaUser } from 'react-icons/fa';
|
||||
import { IoIosLink, IoIosSearch } from 'react-icons/io';
|
||||
import { IoExtensionPuzzleOutline } from 'react-icons/io5';
|
||||
import websiteScreenshot from '../../public/website-screenshot.png';
|
||||
|
||||
import Quotes from 'components/Quotes/Quotes';
|
||||
import styles from 'styles/about.module.scss';
|
||||
|
||||
export default function AboutPage() {
|
||||
const { t } = useTranslation('about');
|
||||
return (
|
||||
<PageTransition className={clsx('App', styles['about-page'])}>
|
||||
<Navbar />
|
||||
<HeroHeader />
|
||||
<div className={styles['page-content']}>
|
||||
<ul className={`reset ${styles['about-list']}`}>
|
||||
<AboutItem
|
||||
icon={AiFillFolderOpen}
|
||||
title={t('about:category.title')}
|
||||
text={t('about:category.text')}
|
||||
/>
|
||||
<AboutItem
|
||||
icon={IoIosLink}
|
||||
title={t('about:link.title')}
|
||||
text={t('about:link.text')}
|
||||
/>
|
||||
<AboutItem
|
||||
icon={IoIosSearch}
|
||||
title={t('about:search.title')}
|
||||
text={t('about:search.text')}
|
||||
/>
|
||||
<AboutItem
|
||||
icon={IoExtensionPuzzleOutline}
|
||||
title={t('about:extension.title')}
|
||||
text={t('about:extension.text')}
|
||||
/>
|
||||
<AboutItem
|
||||
icon={FaUser}
|
||||
title={t('about:contribute.title')}
|
||||
text={t('about:contribute.text')}
|
||||
/>
|
||||
</ul>
|
||||
<h2>{t('about:look-title')}</h2>
|
||||
<div className={styles['screenshot-wrapper']}>
|
||||
<Image
|
||||
src={websiteScreenshot}
|
||||
alt={t('about:website-screenshot-alt')}
|
||||
title={t('about:website-screenshot-alt')}
|
||||
fill
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Footer />
|
||||
</PageTransition>
|
||||
);
|
||||
}
|
||||
|
||||
const AboutItem = ({
|
||||
title,
|
||||
text,
|
||||
icon: Icon,
|
||||
}: {
|
||||
title: string;
|
||||
text: string;
|
||||
icon: IconType;
|
||||
}) => (
|
||||
<li className={styles['about-item']}>
|
||||
<Icon size={60} />
|
||||
<div>{title}</div>
|
||||
<p>{text}</p>
|
||||
</li>
|
||||
);
|
||||
|
||||
function HeroHeader() {
|
||||
const { t } = useTranslation('about');
|
||||
return (
|
||||
<header className={styles['hero']}>
|
||||
<h1>{t('about:hero.title')}</h1>
|
||||
<Quotes>{t('common:slogan')}</Quotes>
|
||||
<Link
|
||||
href='/login'
|
||||
className='reset'
|
||||
>
|
||||
{t('about:hero.cta')}
|
||||
</Link>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps = async ({ locale }) => ({
|
||||
props: {
|
||||
...(await getServerSideTranslation(locale, ['about'])),
|
||||
},
|
||||
});
|
||||
205
src/pages/app.tsx
Normal file
205
src/pages/app.tsx
Normal file
@@ -0,0 +1,205 @@
|
||||
import clsx from 'clsx';
|
||||
import Links from 'components/Links/Links';
|
||||
import PageTransition from 'components/PageTransition';
|
||||
import SideMenu from 'components/SideMenu/SideMenu';
|
||||
import SideNavigation from 'components/SideNavigation/SideNavigation';
|
||||
import * as Keys from 'constants/keys';
|
||||
import PATHS from 'constants/paths';
|
||||
import ActiveCategoryContext from 'contexts/activeCategoryContext';
|
||||
import CategoriesContext from 'contexts/categoriesContext';
|
||||
import FavoritesContext from 'contexts/favoritesContext';
|
||||
import GlobalHotkeysContext from 'contexts/globalHotkeysContext';
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
import { useMediaQuery } from 'hooks/useMediaQuery';
|
||||
import useModal from 'hooks/useModal';
|
||||
import { getServerSideTranslation } from 'i18n';
|
||||
import getUserCategories from 'lib/category/getUserCategories';
|
||||
import sortCategoriesByNextId from 'lib/category/sortCategoriesByNextId';
|
||||
import { useRouter } from 'next/router';
|
||||
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useSwipeable } from 'react-swipeable';
|
||||
import styles from 'styles/home.module.scss';
|
||||
import { CategoryWithLinks, LinkWithCategory } from 'types/types';
|
||||
import { withAuthentication } from 'utils/session';
|
||||
|
||||
interface HomePageProps {
|
||||
categories: CategoryWithLinks[];
|
||||
activeCategory: CategoryWithLinks | undefined;
|
||||
}
|
||||
|
||||
export default function HomePage(props: Readonly<HomePageProps>) {
|
||||
const isMobile = useMediaQuery('(max-width: 768px)');
|
||||
const { isShowing, open, close } = useModal();
|
||||
const handlers = useSwipeable({
|
||||
trackMouse: true,
|
||||
onSwipedRight: open,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!isMobile && isShowing) {
|
||||
close();
|
||||
}
|
||||
}, [close, isMobile, isShowing]);
|
||||
|
||||
return (
|
||||
<PageTransition
|
||||
className={clsx('App', 'flex-row')}
|
||||
hideLangageSelector
|
||||
>
|
||||
<HomeProviders
|
||||
categories={props.categories}
|
||||
activeCategory={props.activeCategory}
|
||||
>
|
||||
<div
|
||||
className={styles['swipe-handler']}
|
||||
{...handlers}
|
||||
>
|
||||
{!isMobile && (
|
||||
<div className={styles['side-bar']}>
|
||||
<SideNavigation />
|
||||
</div>
|
||||
)}
|
||||
<AnimatePresence>
|
||||
{isShowing && (
|
||||
<SideMenu close={close}>
|
||||
<SideNavigation />
|
||||
</SideMenu>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<Links
|
||||
isMobile={isMobile}
|
||||
openSideMenu={open}
|
||||
/>
|
||||
</div>
|
||||
</HomeProviders>
|
||||
</PageTransition>
|
||||
);
|
||||
}
|
||||
|
||||
function HomeProviders(
|
||||
props: Readonly<{
|
||||
children: ReactNode;
|
||||
categories: CategoryWithLinks[];
|
||||
activeCategory: CategoryWithLinks;
|
||||
}>,
|
||||
) {
|
||||
const router = useRouter();
|
||||
const [globalHotkeysEnabled, setGlobalHotkeysEnabled] =
|
||||
useState<boolean>(true);
|
||||
const [categories, setCategories] = useState<CategoryWithLinks[]>(
|
||||
props.categories,
|
||||
);
|
||||
const [activeCategory, setActiveCategory] =
|
||||
useState<CategoryWithLinks | null>(props.activeCategory || categories?.[0]);
|
||||
|
||||
const handleChangeCategory = useCallback(
|
||||
(category: CategoryWithLinks) => {
|
||||
setActiveCategory(category);
|
||||
router.push(`${PATHS.APP}?categoryId=${category.id}`);
|
||||
},
|
||||
[router],
|
||||
);
|
||||
|
||||
const favorites = useMemo<LinkWithCategory[]>(
|
||||
() =>
|
||||
categories.reduce((acc, category) => {
|
||||
category.links.forEach((link) =>
|
||||
link.favorite ? acc.push(link) : null,
|
||||
);
|
||||
return acc;
|
||||
}, [] as LinkWithCategory[]),
|
||||
[categories],
|
||||
);
|
||||
|
||||
const categoriesContextValue = useMemo(
|
||||
() => ({ categories, setCategories }),
|
||||
[categories],
|
||||
);
|
||||
const activeCategoryContextValue = useMemo(
|
||||
() => ({ activeCategory, setActiveCategory: handleChangeCategory }),
|
||||
[activeCategory, handleChangeCategory],
|
||||
);
|
||||
const favoritesContextValue = useMemo(() => ({ favorites }), [favorites]);
|
||||
const globalHotkeysContextValue = useMemo(
|
||||
() => ({
|
||||
globalHotkeysEnabled: globalHotkeysEnabled,
|
||||
setGlobalHotkeysEnabled,
|
||||
}),
|
||||
[globalHotkeysEnabled],
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
Keys.OPEN_CREATE_LINK_KEY,
|
||||
() => {
|
||||
router.push(`${PATHS.LINK.CREATE}?categoryId=${activeCategory.id}`);
|
||||
},
|
||||
{ enabled: globalHotkeysEnabled },
|
||||
);
|
||||
useHotkeys(
|
||||
Keys.OPEN_CREATE_CATEGORY_KEY,
|
||||
() => {
|
||||
router.push(PATHS.CATEGORY.CREATE);
|
||||
},
|
||||
{ enabled: globalHotkeysEnabled },
|
||||
);
|
||||
return (
|
||||
<CategoriesContext.Provider value={categoriesContextValue}>
|
||||
<ActiveCategoryContext.Provider value={activeCategoryContextValue}>
|
||||
<FavoritesContext.Provider value={favoritesContextValue}>
|
||||
<GlobalHotkeysContext.Provider value={globalHotkeysContextValue}>
|
||||
{props.children}
|
||||
</GlobalHotkeysContext.Provider>
|
||||
</FavoritesContext.Provider>
|
||||
</ActiveCategoryContext.Provider>
|
||||
</CategoriesContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps = withAuthentication(
|
||||
async ({ query, session, user, locale }) => {
|
||||
const queryCategoryId = (query?.categoryId as string) || '';
|
||||
const searchQueryParam = (query?.q as string)?.toLowerCase() || '';
|
||||
|
||||
const categories = await getUserCategories(user);
|
||||
if (categories.length === 0) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: PATHS.CATEGORY.CREATE,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const link = categories
|
||||
.map((category) => category.links)
|
||||
.flat()
|
||||
.find(
|
||||
(link: LinkWithCategory) =>
|
||||
link.name.toLowerCase() === searchQueryParam ||
|
||||
link.url.toLowerCase() === searchQueryParam,
|
||||
);
|
||||
if (link) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: link.url,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const activeCategory = categories.find(
|
||||
({ id }) => id === Number(queryCategoryId),
|
||||
);
|
||||
return {
|
||||
props: {
|
||||
session,
|
||||
categories: JSON.parse(
|
||||
JSON.stringify(sortCategoriesByNextId(categories)),
|
||||
),
|
||||
activeCategory: activeCategory
|
||||
? JSON.parse(JSON.stringify(activeCategory))
|
||||
: null,
|
||||
...(await getServerSideTranslation(locale, ['home'])),
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
@@ -44,7 +44,7 @@ export default function PageCreateCategory({
|
||||
body: { name, description, nextId: null },
|
||||
})
|
||||
.then((data) =>
|
||||
router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`),
|
||||
router.push(`${PATHS.APP}?categoryId=${data?.categoryId}`),
|
||||
)
|
||||
.catch(setError)
|
||||
.finally(() => setSubmitted(false));
|
||||
|
||||
@@ -46,7 +46,7 @@ export default function PageEditCategory({
|
||||
body: { name, description, nextId: category.nextId },
|
||||
})
|
||||
.then((data) =>
|
||||
router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`),
|
||||
router.push(`${PATHS.APP}?categoryId=${data?.categoryId}`),
|
||||
)
|
||||
.catch(setError)
|
||||
.finally(() => setSubmitted(false));
|
||||
@@ -91,7 +91,7 @@ export const getServerSideProps = withAuthentication(
|
||||
if (!category) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: PATHS.HOME,
|
||||
destination: PATHS.APP,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export default function PageRemoveCategory({
|
||||
url: `${PATHS.API.CATEGORY}/${category.id}`,
|
||||
method: 'DELETE',
|
||||
})
|
||||
.then(() => router.push(PATHS.HOME))
|
||||
.then(() => router.push(PATHS.APP))
|
||||
.catch(setError)
|
||||
.finally(() => setSubmitted(false));
|
||||
};
|
||||
@@ -100,7 +100,7 @@ export const getServerSideProps = withAuthentication(
|
||||
if (!category) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: PATHS.HOME,
|
||||
destination: PATHS.APP,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,205 +1,105 @@
|
||||
import clsx from 'clsx';
|
||||
import Links from 'components/Links/Links';
|
||||
import Footer from 'components/Footer/Footer';
|
||||
import Navbar from 'components/Navbar/Navbar';
|
||||
import PageTransition from 'components/PageTransition';
|
||||
import SideMenu from 'components/SideMenu/SideMenu';
|
||||
import SideNavigation from 'components/SideNavigation/SideNavigation';
|
||||
import * as Keys from 'constants/keys';
|
||||
import PATHS from 'constants/paths';
|
||||
import ActiveCategoryContext from 'contexts/activeCategoryContext';
|
||||
import CategoriesContext from 'contexts/categoriesContext';
|
||||
import FavoritesContext from 'contexts/favoritesContext';
|
||||
import GlobalHotkeysContext from 'contexts/globalHotkeysContext';
|
||||
import { AnimatePresence } from 'framer-motion';
|
||||
import { useMediaQuery } from 'hooks/useMediaQuery';
|
||||
import useModal from 'hooks/useModal';
|
||||
import { getServerSideTranslation } from 'i18n';
|
||||
import getUserCategories from 'lib/category/getUserCategories';
|
||||
import sortCategoriesByNextId from 'lib/category/sortCategoriesByNextId';
|
||||
import { useRouter } from 'next/router';
|
||||
import { ReactNode, useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useHotkeys } from 'react-hotkeys-hook';
|
||||
import { useSwipeable } from 'react-swipeable';
|
||||
import styles from 'styles/home.module.scss';
|
||||
import { CategoryWithLinks, LinkWithCategory } from 'types/types';
|
||||
import { withAuthentication } from 'utils/session';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import Image from 'next/image';
|
||||
import Link from 'next/link';
|
||||
import { IconType } from 'react-icons';
|
||||
import { AiFillFolderOpen } from 'react-icons/ai';
|
||||
import { FaUser } from 'react-icons/fa';
|
||||
import { IoIosLink, IoIosSearch } from 'react-icons/io';
|
||||
import { IoExtensionPuzzleOutline } from 'react-icons/io5';
|
||||
import websiteScreenshot from '../../public/website-screenshot.png';
|
||||
|
||||
interface HomePageProps {
|
||||
categories: CategoryWithLinks[];
|
||||
activeCategory: CategoryWithLinks | undefined;
|
||||
}
|
||||
|
||||
export default function HomePage(props: Readonly<HomePageProps>) {
|
||||
const isMobile = useMediaQuery('(max-width: 768px)');
|
||||
const { isShowing, open, close } = useModal();
|
||||
const handlers = useSwipeable({
|
||||
trackMouse: true,
|
||||
onSwipedRight: open,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!isMobile && isShowing) {
|
||||
close();
|
||||
}
|
||||
}, [close, isMobile, isShowing]);
|
||||
import Quotes from 'components/Quotes/Quotes';
|
||||
import PATHS from 'constants/paths';
|
||||
import styles from 'styles/about.module.scss';
|
||||
|
||||
export default function AboutPage() {
|
||||
const { t } = useTranslation('about');
|
||||
return (
|
||||
<PageTransition
|
||||
className={clsx('App', 'flex-row')}
|
||||
hideLangageSelector
|
||||
>
|
||||
<HomeProviders
|
||||
categories={props.categories}
|
||||
activeCategory={props.activeCategory}
|
||||
>
|
||||
<div
|
||||
className={styles['swipe-handler']}
|
||||
{...handlers}
|
||||
>
|
||||
{!isMobile && (
|
||||
<div className={styles['side-bar']}>
|
||||
<SideNavigation />
|
||||
</div>
|
||||
)}
|
||||
<AnimatePresence>
|
||||
{isShowing && (
|
||||
<SideMenu close={close}>
|
||||
<SideNavigation />
|
||||
</SideMenu>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
<Links
|
||||
isMobile={isMobile}
|
||||
openSideMenu={open}
|
||||
<PageTransition className={clsx('App', styles['about-page'])}>
|
||||
<Navbar />
|
||||
<HeroHeader />
|
||||
<div className={styles['page-content']}>
|
||||
<ul className={`reset ${styles['about-list']}`}>
|
||||
<AboutItem
|
||||
icon={AiFillFolderOpen}
|
||||
title={t('about:category.title')}
|
||||
text={t('about:category.text')}
|
||||
/>
|
||||
<AboutItem
|
||||
icon={IoIosLink}
|
||||
title={t('about:link.title')}
|
||||
text={t('about:link.text')}
|
||||
/>
|
||||
<AboutItem
|
||||
icon={IoIosSearch}
|
||||
title={t('about:search.title')}
|
||||
text={t('about:search.text')}
|
||||
/>
|
||||
<AboutItem
|
||||
icon={IoExtensionPuzzleOutline}
|
||||
title={t('about:extension.title')}
|
||||
text={t('about:extension.text')}
|
||||
/>
|
||||
<AboutItem
|
||||
icon={FaUser}
|
||||
title={t('about:contribute.title')}
|
||||
text={t('about:contribute.text')}
|
||||
/>
|
||||
</ul>
|
||||
<h2>{t('about:look-title')}</h2>
|
||||
<div className={styles['screenshot-wrapper']}>
|
||||
<Image
|
||||
src={websiteScreenshot}
|
||||
alt={t('about:website-screenshot-alt')}
|
||||
title={t('about:website-screenshot-alt')}
|
||||
fill
|
||||
/>
|
||||
</div>
|
||||
</HomeProviders>
|
||||
</div>
|
||||
<Footer />
|
||||
</PageTransition>
|
||||
);
|
||||
}
|
||||
|
||||
function HomeProviders(
|
||||
props: Readonly<{
|
||||
children: ReactNode;
|
||||
categories: CategoryWithLinks[];
|
||||
activeCategory: CategoryWithLinks;
|
||||
}>,
|
||||
) {
|
||||
const router = useRouter();
|
||||
const [globalHotkeysEnabled, setGlobalHotkeysEnabled] =
|
||||
useState<boolean>(true);
|
||||
const [categories, setCategories] = useState<CategoryWithLinks[]>(
|
||||
props.categories,
|
||||
);
|
||||
const [activeCategory, setActiveCategory] =
|
||||
useState<CategoryWithLinks | null>(props.activeCategory || categories?.[0]);
|
||||
const AboutItem = ({
|
||||
title,
|
||||
text,
|
||||
icon: Icon,
|
||||
}: {
|
||||
title: string;
|
||||
text: string;
|
||||
icon: IconType;
|
||||
}) => (
|
||||
<li className={styles['about-item']}>
|
||||
<Icon size={60} />
|
||||
<div>{title}</div>
|
||||
<p>{text}</p>
|
||||
</li>
|
||||
);
|
||||
|
||||
const handleChangeCategory = useCallback(
|
||||
(category: CategoryWithLinks) => {
|
||||
setActiveCategory(category);
|
||||
router.push(`${PATHS.HOME}?categoryId=${category.id}`);
|
||||
},
|
||||
[router],
|
||||
);
|
||||
|
||||
const favorites = useMemo<LinkWithCategory[]>(
|
||||
() =>
|
||||
categories.reduce((acc, category) => {
|
||||
category.links.forEach((link) =>
|
||||
link.favorite ? acc.push(link) : null,
|
||||
);
|
||||
return acc;
|
||||
}, [] as LinkWithCategory[]),
|
||||
[categories],
|
||||
);
|
||||
|
||||
const categoriesContextValue = useMemo(
|
||||
() => ({ categories, setCategories }),
|
||||
[categories],
|
||||
);
|
||||
const activeCategoryContextValue = useMemo(
|
||||
() => ({ activeCategory, setActiveCategory: handleChangeCategory }),
|
||||
[activeCategory, handleChangeCategory],
|
||||
);
|
||||
const favoritesContextValue = useMemo(() => ({ favorites }), [favorites]);
|
||||
const globalHotkeysContextValue = useMemo(
|
||||
() => ({
|
||||
globalHotkeysEnabled: globalHotkeysEnabled,
|
||||
setGlobalHotkeysEnabled,
|
||||
}),
|
||||
[globalHotkeysEnabled],
|
||||
);
|
||||
|
||||
useHotkeys(
|
||||
Keys.OPEN_CREATE_LINK_KEY,
|
||||
() => {
|
||||
router.push(`${PATHS.LINK.CREATE}?categoryId=${activeCategory.id}`);
|
||||
},
|
||||
{ enabled: globalHotkeysEnabled },
|
||||
);
|
||||
useHotkeys(
|
||||
Keys.OPEN_CREATE_CATEGORY_KEY,
|
||||
() => {
|
||||
router.push(PATHS.CATEGORY.CREATE);
|
||||
},
|
||||
{ enabled: globalHotkeysEnabled },
|
||||
);
|
||||
function HeroHeader() {
|
||||
const { t } = useTranslation('about');
|
||||
return (
|
||||
<CategoriesContext.Provider value={categoriesContextValue}>
|
||||
<ActiveCategoryContext.Provider value={activeCategoryContextValue}>
|
||||
<FavoritesContext.Provider value={favoritesContextValue}>
|
||||
<GlobalHotkeysContext.Provider value={globalHotkeysContextValue}>
|
||||
{props.children}
|
||||
</GlobalHotkeysContext.Provider>
|
||||
</FavoritesContext.Provider>
|
||||
</ActiveCategoryContext.Provider>
|
||||
</CategoriesContext.Provider>
|
||||
<header className={styles['hero']}>
|
||||
<h1>{t('about:hero.title')}</h1>
|
||||
<Quotes>{t('common:slogan')}</Quotes>
|
||||
<Link
|
||||
href={PATHS.APP}
|
||||
className='reset'
|
||||
>
|
||||
{t('about:hero.cta')}
|
||||
</Link>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
|
||||
export const getServerSideProps = withAuthentication(
|
||||
async ({ query, session, user, locale }) => {
|
||||
const queryCategoryId = (query?.categoryId as string) || '';
|
||||
const searchQueryParam = (query?.q as string)?.toLowerCase() || '';
|
||||
|
||||
const categories = await getUserCategories(user);
|
||||
if (categories.length === 0) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: PATHS.CATEGORY.CREATE,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const link = categories
|
||||
.map((category) => category.links)
|
||||
.flat()
|
||||
.find(
|
||||
(link: LinkWithCategory) =>
|
||||
link.name.toLowerCase() === searchQueryParam ||
|
||||
link.url.toLowerCase() === searchQueryParam,
|
||||
);
|
||||
if (link) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: link.url,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const activeCategory = categories.find(
|
||||
({ id }) => id === Number(queryCategoryId),
|
||||
);
|
||||
return {
|
||||
props: {
|
||||
session,
|
||||
categories: JSON.parse(
|
||||
JSON.stringify(sortCategoriesByNextId(categories)),
|
||||
),
|
||||
activeCategory: activeCategory
|
||||
? JSON.parse(JSON.stringify(activeCategory))
|
||||
: null,
|
||||
...(await getServerSideTranslation(locale, ['home'])),
|
||||
},
|
||||
};
|
||||
export const getServerSideProps = async ({ locale }) => ({
|
||||
props: {
|
||||
...(await getServerSideTranslation(locale, ['about'])),
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
@@ -59,7 +59,7 @@ export default function PageCreateLink({
|
||||
body: { name, url, description, favorite, categoryId },
|
||||
})
|
||||
.then((data) =>
|
||||
router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`),
|
||||
router.push(`${PATHS.APP}?categoryId=${data?.categoryId}`),
|
||||
)
|
||||
.catch(setError)
|
||||
.finally(() => setSubmitted(false));
|
||||
@@ -129,7 +129,7 @@ export const getServerSideProps = withAuthentication(
|
||||
if (categories.length === 0) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: PATHS.HOME,
|
||||
destination: PATHS.APP,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ export default function PageEditLink({
|
||||
body: { name, url, description, favorite, categoryId },
|
||||
})
|
||||
.then((data) =>
|
||||
router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`),
|
||||
router.push(`${PATHS.APP}?categoryId=${data?.categoryId}`),
|
||||
)
|
||||
.catch(setError)
|
||||
.finally(() => setSubmitted(false));
|
||||
@@ -139,7 +139,7 @@ export const getServerSideProps = withAuthentication(
|
||||
if (!link) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: PATHS.HOME,
|
||||
destination: PATHS.APP,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ export default function PageRemoveLink({
|
||||
method: 'DELETE',
|
||||
})
|
||||
.then((data) =>
|
||||
router.push(`${PATHS.HOME}?categoryId=${data?.categoryId}`),
|
||||
router.push(`${PATHS.APP}?categoryId=${data?.categoryId}`),
|
||||
)
|
||||
.catch(setError)
|
||||
.finally(() => setSubmitted(false));
|
||||
@@ -108,7 +108,7 @@ export const getServerSideProps = withAuthentication(
|
||||
if (!link) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: PATHS.HOME,
|
||||
destination: PATHS.APP,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@ export async function getServerSideProps({ req, res, locale }) {
|
||||
if (user) {
|
||||
return {
|
||||
redirect: {
|
||||
destination: PATHS.HOME,
|
||||
destination: PATHS.APP,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user