diff --git a/public/locales/en/home.json b/public/locales/en/home.json index 97a095a..65ed406 100644 --- a/public/locales/en/home.json +++ b/public/locales/en/home.json @@ -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" diff --git a/public/locales/en/periodical.json b/public/locales/en/periodical.json index 2dae9a3..915c9fc 100644 --- a/public/locales/en/periodical.json +++ b/public/locales/en/periodical.json @@ -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 category. You are currently viewing the", "p_text2": "collection." }, diff --git a/public/locales/zh/periodical.json b/public/locales/zh/periodical.json index 11ebafe..b8f3f96 100644 --- a/public/locales/zh/periodical.json +++ b/public/locales/zh/periodical.json @@ -21,7 +21,10 @@ "page_next": "下一页", "category": { "title": "《HelloGitHub 月刊》{{name}} 集合", - "nav": "分类", + "nav": { + "active": "活跃", + "last": "默认" + }, "p_text": "这里是按照「分类」阅读往期的 HelloGitHub 月刊内容, 您目前在查看", "p_text2": "集合。" }, diff --git a/src/components/navbar/ArticleBar.tsx b/src/components/navbar/ArticleBar.tsx index 7d10b2a..0806cbb 100644 --- a/src/components/navbar/ArticleBar.tsx +++ b/src/components/navbar/ArticleBar.tsx @@ -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, } ); @@ -41,10 +41,11 @@ const ArticleNavbar = ({ t }: Props) => {
{t('nav.title')}
-
+
{t('nav.last')} + {t('nav.hot')} diff --git a/src/components/navbar/CategoryBar.tsx b/src/components/navbar/CategoryBar.tsx new file mode 100644 index 0000000..13ffacf --- /dev/null +++ b/src/components/navbar/CategoryBar.tsx @@ -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 ( +
+
+
+ +
+
+ {middleText} +
+ +
+
+ ); +}; + +export default CategoryNavbar; diff --git a/src/components/periodical/item.tsx b/src/components/periodical/item.tsx index 788598a..c510455 100644 --- a/src/components/periodical/item.tsx +++ b/src/components/periodical/item.tsx @@ -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'; @@ -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 = ( +
+ + {text} +
+ ); + + if (link) { + return ( + + {content} + + ); + } + + return content; +}; + const PeriodItem: NextPage = ({ item, index }) => { const { t, i18n } = useTranslation('periodical'); @@ -31,7 +60,7 @@ const PeriodItem: NextPage = ({ item, index }) => {
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}
@@ -39,18 +68,26 @@ const PeriodItem: NextPage = ({ item, index }) => {
{/* stars forks watch */}
-
- - Star {numFormat(item.stars, 1)} -
-
- - Fork {numFormat(item.forks, 1)} -
-
- - {fromNow(item.publish_at, i18n.language)} -
+ + + {item.updated_at && ( + + )} +
diff --git a/src/pages/periodical/category/[name].tsx b/src/pages/periodical/category/[name].tsx index 419fd97..7aab5ca 100644 --- a/src/pages/periodical/category/[name].tsx +++ b/src/pages/periodical/category/[name].tsx @@ -4,7 +4,7 @@ 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'; @@ -12,6 +12,7 @@ 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 { @@ -20,7 +21,10 @@ import { PeriodicalItem, } from '@/types/periodical'; -const PeriodicalCategoryPage: NextPage = ({ category }) => { +const PeriodicalCategoryPage: NextPage = ({ + category, + sortBy, +}) => { const { t, i18n } = useTranslation('periodical'); const router = useRouter(); @@ -45,9 +49,13 @@ const PeriodicalCategoryPage: NextPage = ({ 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) { @@ -55,8 +63,7 @@ const PeriodicalCategoryPage: NextPage = ({ category }) => {
-

- +

  • @@ -72,8 +79,11 @@ const PeriodicalCategoryPage: NextPage = ({ category }) => { <>
    - - +
    @@ -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 { @@ -135,6 +147,7 @@ export const getServerSideProps: GetServerSideProps = async ({ return { props: { category: data, + sortBy: sortBy, ...(await serverSideTranslations(locale as string, [ 'common', 'periodical', diff --git a/src/services/category.ts b/src/services/category.ts index d8e996c..fe65611 100644 --- a/src/services/category.ts +++ b/src/services/category.ts @@ -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 => { - 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(url, req); return data; } catch (error) { diff --git a/src/types/periodical.tsx b/src/types/periodical.tsx index c0d2be8..60564fd 100644 --- a/src/types/periodical.tsx +++ b/src/types/periodical.tsx @@ -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 { @@ -54,6 +56,7 @@ export type VolumeAll = { export interface CategoryPageProps { category: Category; + sortBy: string | null; } export interface Category {