Skip to content

Commit

Permalink
feat: add contribution ranking
Browse files Browse the repository at this point in the history
  • Loading branch information
521xueweihan committed Nov 4, 2024
1 parent 441a3c2 commit ab8cc9a
Show file tree
Hide file tree
Showing 8 changed files with 287 additions and 1 deletion.
13 changes: 13 additions & 0 deletions public/locales/en/rank.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,18 @@
"nginx": "A free, open-source, lightweight, high-performance web server developed by Igor Sysoev for Rambler.ru, the second most visited site in Russia.",
"openresty": "A web platform based on Nginx that can run Lua scripts using its LuaJIT engine, created by Yichun Zhang."
}
},
"contribution": {
"title": "Contribution Ranking",
"nav": "{{month}} {{year}} Contribution Ranking",
"p_text": "<strong>HelloGitHub Contribution Ranking</strong> is a ranking based on the contribution value of users in the HelloGitHub open source community, updated with the current month's data on the 15th of each month. Users can earn contribution points in the following ways: sharing an open source project earns 5 points, posting a comment earns 2 points, and if a comment is selected as a hot comment, an additional 10 contribution points will be awarded.",
"thead": {
"position": "Rank",
"name": "User",
"rating": "Level",
"change": "Change",
"total": "Total",
"md_change": "📊"
}
}
}
13 changes: 13 additions & 0 deletions public/locales/zh/rank.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,18 @@
"nginx": "免费开源、轻量级、高性能 Web 服务器,由伊戈尔·赛索耶夫为俄罗斯访问量第二的 Rambler.ru 站点开发。",
"openresty": "一个基于 Nginx 的 Web 平台,可以使用其 LuaJIT 引擎运行 Lua 脚本,由章亦春创建。"
}
},
"contribution": {
"title": "用户贡献排名",
"nav": "{{year}} 年 {{month}} 月用户贡献排行榜",
"p_text": "<strong>「HelloGitHub 贡献排名」</strong>是根据用户在 HelloGitHub 开源社区中的贡献值进行排名,每月 15 日更新当月的数据。用户可通过以下方式获得贡献值:分享开源项目可获得 5 点,发表评论获得 2 点,若评论被选为热评,则可额外获得 10 点贡献值。",
"thead": {
"position": "排名",
"name": "用户",
"rating": "等级",
"change": "对比上月",
"total": "总贡献",
"md_change": "本月"
}
}
}
4 changes: 3 additions & 1 deletion src/components/rankTable/RankTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const RankTable = ({ columns, list, i18n_lang }: TableProps) => {
return (
<td
key={key}
className='truncate whitespace-nowrap bg-white px-3 py-2 text-left text-sm font-medium text-gray-800 dark:bg-gray-800 dark:text-gray-300 md:px-6 md:py-4'
className='truncate whitespace-nowrap bg-white px-4 py-2 text-left text-sm font-medium text-gray-800 dark:bg-gray-800 dark:text-gray-300 md:px-6 md:py-4'
>
{content}
</td>
Expand Down Expand Up @@ -114,11 +114,13 @@ export const RankSearchBar = ({
return i18n_lang == 'en'
? [
{ key: '/report/tiobe', value: 'Language' },
{ key: '/report/contribution', value: 'Contribution' },
{ key: '/report/netcraft', value: 'Server' },
{ key: '/report/db-engines', value: 'Database' },
]
: [
{ key: '/report/tiobe', value: '编程语言' },
{ key: '/report/contribution', value: '用户贡献' },
{ key: '/report/netcraft', value: '服务器' },
{ key: '/report/db-engines', value: '数据库' },
];
Expand Down
54 changes: 54 additions & 0 deletions src/components/report/Report.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { AiFillCaretDown, AiFillCaretUp } from 'react-icons/ai';
import { IoMdRemove, IoMdTrendingDown, IoMdTrendingUp } from 'react-icons/io';

import { NoPrefetchLink } from '../links/CustomLink';

import { RankDataItem } from '@/types/rank';

export const ChangeColumnRender = (
Expand Down Expand Up @@ -54,3 +56,55 @@ export const TrendColumnRender = (
}
return <span>{icon}</span>;
};

export const ContributionColumnRender = (
row: RankDataItem,
i18n_lang: string
) => {
let text = '-';
if (row.change !== null) {
text = `+${row.change}`;
} else {
return <span>{text}</span>;
}

return (
<div className='flex items-center'>
{i18n_lang === 'en' ? (
<span className='text-green-500'>{text}</span>
) : (
<span className='text-red-500'>{text}</span>
)}
</div>
);
};

export const UserColumnRender = (row: RankDataItem) => {
return (
<NoPrefetchLink href={`/user/${row.uid}`}>
<div className='flex cursor-pointer items-center'>
<img
width='20'
height='20'
src={row.avatar}
alt={`${row.name} avatar`}
className='block rounded'
/>
<span className='ml-1 '>{row.name}</span>
</div>
</NoPrefetchLink>
);
};

export const PositionColumnRender = (row: RankDataItem) => {
const positionList = ['🥇', '🥈', '🥉'];
return (
<div className='flex items-center'>
{row.position > 3 ? (
<span>{row.position}</span>
) : (
<span>{positionList[row.position - 1]}</span>
)}
</div>
);
};
179 changes: 179 additions & 0 deletions src/pages/report/contribution.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
import { GetServerSideProps, NextPage } from 'next';
import { useRouter } from 'next/router';
import { Trans, useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
import { useMemo } from 'react';

import Loading from '@/components/loading/Loading';
import Navbar from '@/components/navbar/Navbar';
import {
getMonthName,
RankSearchBar,
RankTable,
} from '@/components/rankTable/RankTable';
import {
ContributionColumnRender,
PositionColumnRender,
UserColumnRender,
} from '@/components/report/Report';
import Seo from '@/components/Seo';

import { getContributionRank } from '@/services/rank';
import { getClientIP } from '@/utils/util';

import { RankPageProps } from '@/types/rank';

const ContributionPage: NextPage<RankPageProps> = ({
year,
month,
monthList,
list,
}) => {
const { t, i18n } = useTranslation('rank');
const router = useRouter();

const onSearch = (key: string, value: string) => {
if (key === 'month') {
router.push(`/report/contribution/?month=${value}`);
}
if (key === 'target') {
router.push(`${value}`);
}
};

// 排名 用户 等级 贡献值 对比上月
const columns: any[] = useMemo(
() => [
{
key: 'position',
title: t('contribution.thead.position'),
render: PositionColumnRender,
width: 80,
},
{
key: 'name',
title: t('contribution.thead.name'),
render: UserColumnRender,
width: 180,
},
{ key: 'rating', title: t('contribution.thead.rating') },
{
key: 'change',
title: t('contribution.thead.change'),
render: ContributionColumnRender,
},
{
key: 'total',
title: t('contribution.thead.total'),
},
],
[i18n.language]
);

// 排名 用户 本月 贡献值
const md_columns: any[] = useMemo(
() =>
columns
.map((col) => {
if (col.key === 'position') {
return { ...col, width: 60 };
}
if (col.key === 'name') {
return { ...col, width: 140 };
}
if (col.key === 'change') {
return {
...col,
title: t('contribution.thead.md_change'),
render: ContributionColumnRender,
width: 80,
};
}
if (col.key === 'rating') {
return null;
}
return col;
})
.filter(Boolean),
[i18n.language]
);

return (
<>
<Seo title={t('contribution.title')} />
{list ? (
<div>
<Navbar
middleText={t('contribution.nav', {
year: year,
month: getMonthName(month, i18n.language, { forceEnglish: true }),
})}
/>

<div className='my-2 bg-white px-2 pt-2 dark:bg-gray-800 md:rounded-lg'>
<RankSearchBar
title='HelloGitHub'
logo='https://img.hellogithub.com/logo/logo.png'
i18n_lang={i18n.language}
monthList={monthList}
onChange={onSearch}
/>
<div className='md:hidden'>
<RankTable
columns={md_columns}
list={list}
i18n_lang={i18n.language}
/>
</div>
<div className='hidden md:block'>
<RankTable
columns={columns}
list={list}
i18n_lang={i18n.language}
/>
</div>
<div className='mt-2 rounded-lg border bg-white p-2 text-sm dark:border-gray-700 dark:bg-gray-800 dark:text-gray-300'>
<div className='whitespace-pre-wrap leading-8'>
<p>
<Trans ns='rank' i18nKey='contribution.p_text' />
</p>
</div>
</div>
<div className='h-2' />
</div>
</div>
) : (
<Loading />
)}
</>
);
};

export const getServerSideProps: GetServerSideProps = async ({
query,
req,
locale,
}) => {
const ip = getClientIP(req);
const data = await getContributionRank(
ip,
query['month'] as unknown as number
);
if (!data.success) {
return {
notFound: true,
};
} else {
return {
props: {
year: data.year,
month: data.month,
list: data.data,
monthList: data.month_list,
...(await serverSideTranslations(locale as string, ['common', 'rank'])),
},
};
}
};

export default ContributionPage;
3 changes: 3 additions & 0 deletions src/pages/report/db-engines.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ const DBEnginesPage: NextPage<RankPageProps> = ({
if (col.key === 'position') {
return { ...col, width: 60 };
}
if (col.key === 'name') {
return { ...col, width: 160 };
}
if (col.key === 'rating') {
return { ...col, width: 80 };
}
Expand Down
20 changes: 20 additions & 0 deletions src/services/rank.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,3 +63,23 @@ export const getDBRank = async (
return {} as RankData;
}
};

// 贡献值排名
export const getContributionRank = async (
ip: string,
month?: number
): Promise<RankData> => {
const req: RequestInit = {};
req.headers = { 'x-real-ip': ip, 'x-forwarded-for': ip };

let url = '/report/contribution/';
if (month) {
url = `${url}?month=${month}`;
}
try {
const data = await fetcher<RankData>(makeUrl(url), req);
return data;
} catch (error) {
return {} as RankData;
}
};
2 changes: 2 additions & 0 deletions src/types/rank.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export interface RankDataItem {
star?: string;
percent?: boolean;
total?: number;
avatar?: string;
uid?: string;
}

export interface RankData {
Expand Down

0 comments on commit ab8cc9a

Please sign in to comment.