Skip to content

Commit

Permalink
feat: 阅读月刊分类支持排序
Browse files Browse the repository at this point in the history
  • Loading branch information
521xueweihan committed Jan 8, 2025
1 parent 63d2028 commit 27d8dc3
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 41 deletions.
2 changes: 1 addition & 1 deletion public/locales/en/home.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"bottom_text_nologin": "End of the page! Sign in to read more",
"nav": {
"all": "All",
"newest": "Newest",
"newest": "Latest",
"monthly": "Monthly",
"yearly": "Yearly",
"featured": "Featured"
Expand Down
5 changes: 4 additions & 1 deletion public/locales/en/periodical.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
"page_next": "Next",
"category": {
"title": "HelloGitHub Monthly {{name}} Collection",
"nav": "Category",
"nav": {
"active": "Active",
"last": "Default"
},
"p_text": "Here you can read past volumes of HelloGitHub Monthly by <strong>category</strong>. You are currently viewing the",
"p_text2": "collection."
},
Expand Down
5 changes: 4 additions & 1 deletion public/locales/zh/periodical.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
"page_next": "下一页",
"category": {
"title": "《HelloGitHub 月刊》{{name}} 集合",
"nav": "分类",
"nav": {
"active": "活跃",
"last": "默认"
},
"p_text": "这里是按照「<strong>分类</strong>」阅读往期的 HelloGitHub 月刊内容, 您目前在查看",
"p_text2": "集合。"
},
Expand Down
9 changes: 5 additions & 4 deletions src/components/navbar/ArticleBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ const ArticleNavbar = ({ t }: Props) => {

const linkClassName = (sortName: string) =>
classNames(
'flex items-center whitespace-nowrap rounded-lg px-2 py-1 text-xs hover:text-blue-500 dark:hover:bg-gray-700',
'flex items-center whitespace-nowrap rounded-lg text-xs hover:text-blue-500',
{
'text-gray-500 dark:text-gray-200': sort_by !== sortName,
'dark:bg-gray-700 text-blue-500': sort_by === sortName,
'text-gray-500 dark:text-gray-300': sort_by !== sortName,
'text-blue-500': sort_by === sortName,
}
);

Expand All @@ -41,10 +41,11 @@ const ArticleNavbar = ({ t }: Props) => {
<div className='w-3/4 truncate text-center font-bold dark:text-gray-300'>
{t('nav.title')}
</div>
<div className='flex justify-end text-sm text-gray-500 dark:text-gray-400'>
<div className='flex justify-end gap-2.5 text-sm text-gray-500 dark:text-gray-400'>
<NoPrefetchLink href='/article?sort_by=last'>
<a className={linkClassName('last')}>{t('nav.last')}</a>
</NoPrefetchLink>
<span className='border-r border-gray-100 dark:border-gray-700' />
<NoPrefetchLink href='/article?sort_by=hot'>
<a className={linkClassName('hot')}>{t('nav.hot')}</a>
</NoPrefetchLink>
Expand Down
70 changes: 70 additions & 0 deletions src/components/navbar/CategoryBar.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import classNames from 'classnames';
import { useRouter } from 'next/router';
import { AiOutlineArrowLeft } from 'react-icons/ai';

import { NoPrefetchLink } from '@/components/links/CustomLink';

type Props = {
category: string;
middleText: string;
t: (key: string) => string;
};

const CategoryNavbar = ({ category, middleText, t }: Props) => {
const router = useRouter();
const { sort_by = 'last' } = router.query;

const goBack = () => {
if (window.history.length < 2) {
router.push('/');
} else {
router.back();
}
};

const linkClassName = (sortName: string) =>
classNames(
'flex items-center whitespace-nowrap rounded-lg text-xs hover:text-blue-500',
{
'text-gray-500 dark:text-gray-300': sort_by !== sortName,
'text-blue-500': sort_by === sortName,
}
);

return (
<div className='relative my-2 bg-white dark:bg-gray-800 md:rounded-lg'>
<div className='flex h-12 items-center justify-between py-2 px-4'>
<div className='cursor-pointer' onClick={goBack}>
<AiOutlineArrowLeft
className='text-gray-500 hover:text-blue-400'
size={18}
/>
</div>
<div className='w-3/4 truncate text-center font-bold dark:text-gray-300'>
{middleText}
</div>
<div className='flex justify-end gap-2.5 text-sm text-gray-500 dark:text-gray-400'>
<NoPrefetchLink
href={`/periodical/category/${encodeURIComponent(
category
)}?sort_by=last`}
>
<a className={linkClassName('last')}>{t('category.nav.last')}</a>
</NoPrefetchLink>
<span className='border-r border-gray-100 dark:border-gray-700' />
<NoPrefetchLink
href={`/periodical/category/${encodeURIComponent(
category
)}?sort_by=active`}
>
<a className={linkClassName('active')}>
{t('category.nav.active')}
</a>
</NoPrefetchLink>
</div>
</div>
</div>
);
};

export default CategoryNavbar;
65 changes: 51 additions & 14 deletions src/components/periodical/item.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NextPage } from 'next';
import { useTranslation } from 'next-i18next';
import { GoClock, GoRepoForked } from 'react-icons/go';
import { GoCalendar, GoClock, GoRepoForked } from 'react-icons/go';
import { IoIosStarOutline } from 'react-icons/io';

import { CustomLink, NoPrefetchLink } from '@/components/links/CustomLink';
Expand All @@ -15,6 +15,35 @@ import { MDRender } from '../mdRender/MDRender';

import { PeriodicalItem, PeriodicalItemProps } from '@/types/periodical';

const InfoItem = ({
icon: Icon,
text,
link,
className = '',
}: {
icon: React.ElementType;
text: string | number;
link?: string;
className?: string;
}) => {
const content = (
<div className={`mr-2 flex items-center ${className}`}>
<Icon size={15} className='mr-0.5' />
{text}
</div>
);

if (link) {
return (
<CustomLink href={link} className='hover:text-blue-500'>
{content}
</CustomLink>
);
}

return content;
};

const PeriodItem: NextPage<PeriodicalItemProps> = ({ item, index }) => {
const { t, i18n } = useTranslation('periodical');

Expand All @@ -31,26 +60,34 @@ const PeriodItem: NextPage<PeriodicalItemProps> = ({ item, index }) => {
<CustomLink href={item.github_url} className='truncate'>
<div
onClick={() => onClickLink(item)}
className='truncate text-ellipsis text-xl capitalize text-blue-500 hover:underline active:text-blue-500'
className='truncate text-ellipsis text-xl text-blue-500 hover:underline active:text-blue-500'
>
{item.name}
</div>
</CustomLink>
</div>
{/* stars forks watch */}
<div className='flex flex-row text-sm text-gray-500 dark:text-gray-400 md:text-base'>
<div className='mr-2 flex items-center'>
<IoIosStarOutline size={15} className='mr-0.5' />
Star {numFormat(item.stars, 1)}
</div>
<div className='mr-2 flex items-center'>
<GoRepoForked size={15} className='mr-0.5' />
Fork {numFormat(item.forks, 1)}
</div>
<div className='mr-2 flex items-center'>
<GoClock size={15} className='mr-0.5' />
{fromNow(item.publish_at, i18n.language)}
</div>
<InfoItem
icon={IoIosStarOutline}
text={`Star ${numFormat(item.stars, 1)}`}
/>
<InfoItem
icon={GoRepoForked}
text={`Fork ${numFormat(item.forks, 1)}`}
className='hidden md:flex'
/>
{item.updated_at && (
<InfoItem
icon={GoCalendar}
text={`Vol.${item.volume_num}`}
link={`/periodical/volume/${item.volume_num}`}
/>
)}
<InfoItem
icon={GoClock}
text={fromNow(item.updated_at || item.publish_at, i18n.language)}
/>
</div>
</div>
<div className='flex h-14 flex-1 flex-row items-center justify-end pr-1'>
Expand Down
35 changes: 24 additions & 11 deletions src/pages/periodical/category/[name].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@ import { Trans, useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useMemo } from 'react';

import Navbar from '@/components/navbar/Navbar';
import CategoryNavbar from '@/components/navbar/CategoryBar';
import Pagination from '@/components/pagination/Pagination';
import PeriodItem from '@/components/periodical/item';
import Seo from '@/components/Seo';
import ToTop from '@/components/toTop/ToTop';

import { getCategory } from '@/services/category';
import { nameMap } from '@/utils/constants';
import stringify from '@/utils/qs-stringify';
import { getClientIP } from '@/utils/util';

import {
Expand All @@ -20,7 +21,10 @@ import {
PeriodicalItem,
} from '@/types/periodical';

const PeriodicalCategoryPage: NextPage<CategoryPageProps> = ({ category }) => {
const PeriodicalCategoryPage: NextPage<CategoryPageProps> = ({
category,
sortBy,
}) => {
const { t, i18n } = useTranslation('periodical');

const router = useRouter();
Expand All @@ -45,18 +49,21 @@ const PeriodicalCategoryPage: NextPage<CategoryPageProps> = ({ category }) => {

const onPageChange = (page: number) => {
const name = category?.category_name;
router.push(
`/periodical/category/${encodeURIComponent(name)}?page=${page}`
);
const nextURL = `/periodical/category/${encodeURIComponent(
name
)}?${stringify({
page: page,
sort_by: sortBy ? sortBy : null,
})}`;
router.push(nextURL);
};

if (router.isFallback) {
return (
<div className='mt-20 flex animate-pulse'>
<Seo title='HelloGitHub 月刊' />
<div className='ml-4 mt-2 w-full'>
<h3 className='h-4 rounded-md bg-gray-200'></h3>

<h3 className='h-4 rounded-md bg-gray-200' />
<ul className='mt-5 space-y-3'>
<li className='h-4 w-full rounded-md bg-gray-200' />
<li className='h-4 w-full rounded-md bg-gray-200' />
Expand All @@ -72,8 +79,11 @@ const PeriodicalCategoryPage: NextPage<CategoryPageProps> = ({ category }) => {
<>
<Seo title={t('category.title', { name: categoryItem.name })} />
<div className='relative pb-6'>
<Navbar middleText={categoryItem.name} endText={t('category.nav')} />

<CategoryNavbar
category={category.category_name}
middleText={categoryItem.name}
t={t}
/>
<div className='my-2 bg-white p-4 dark:bg-gray-800 md:rounded-lg'>
<div className='text-normal mb-4 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-300'>
<div className='whitespace-pre-wrap rounded-lg border bg-white p-2 font-normal leading-8 text-gray-500 dark:bg-gray-800 dark:text-gray-300'>
Expand Down Expand Up @@ -122,10 +132,12 @@ export const getServerSideProps: GetServerSideProps = async ({
}) => {
const ip = getClientIP(req);
const name = query['name'] as string;
const sortBy = query['sort_by']?.toString() ?? null;
const data = await getCategory(
ip,
encodeURIComponent(name),
query['page'] as unknown as number
name,
query['page'] as unknown as number,
sortBy
);
if (!data.success) {
return {
Expand All @@ -135,6 +147,7 @@ export const getServerSideProps: GetServerSideProps = async ({
return {
props: {
category: data,
sortBy: sortBy,
...(await serverSideTranslations(locale as string, [
'common',
'periodical',
Expand Down
21 changes: 12 additions & 9 deletions src/services/category.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,21 @@ import { Category } from '@/types/periodical';
export const getCategory = async (
ip: string,
name: string,
page: number
page: number,
sortBy: string | null
): Promise<Category> => {
const req: RequestInit = {};
req.headers = { 'x-real-ip': ip, 'x-forwarded-for': ip };
const req: RequestInit = {
headers: {
'x-real-ip': ip,
'x-forwarded-for': ip,
},
};

try {
let url;
if (page > 1) {
url = makeUrl(`/periodical/category/${name}?page=${page}`);
} else {
url = makeUrl(`/periodical/category/${name}`);
}
const url = makeUrl(`/periodical/category/${encodeURIComponent(name)}`, {
page: page > 1 ? page : null, // 仅当 page > 1 时添加该参数
sort_by: sortBy ? sortBy : null,
});
const data = await fetcher<Category>(url, req);
return data;
} catch (error) {
Expand Down
3 changes: 3 additions & 0 deletions src/types/periodical.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,9 @@ export interface PeriodicalItem {
watch: number;
image_url: string | null;
vote_total: number;
volume_num: number;
publish_at: string;
updated_at: string;
}

export interface PeriodicalItemProps {
Expand All @@ -54,6 +56,7 @@ export type VolumeAll = {

export interface CategoryPageProps {
category: Category;
sortBy: string | null;
}

export interface Category {
Expand Down

0 comments on commit 27d8dc3

Please sign in to comment.