Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feat(contributor-button): 添加数据未找到组件并初始化React模块- 创建DataNotFound组件,用于在未找到数据时展示可能的原因。- 在index.ts中引入contributor_button模块,确保其在页面加载时被初始化。
- 初始化contributor_button模块的React组件,包括状态管理、数据获取和视图渲染逻辑。
- 添加必要的类型定义和全局变量,以支持贡献者按钮功能的正确运行。
- 通过ReactModal和Graph组件增强用户界面,提供切换视图的功能。

在贡献者提供了贡献者网络视图 Active Developer Collaboration Network,以及切换按钮,通过点击可以切换贡献者展示视图.增强了对GitHub仓库页面中贡献者列表的展示功能,提供了更丰富的交互体验。
  • Loading branch information
MrH233 authored and johnathon0715 committed Jul 30, 2024
1 parent 4a09f0d commit f14faf9
Show file tree
Hide file tree
Showing 10 changed files with 367 additions and 3 deletions.
5 changes: 5 additions & 0 deletions src/api/repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const metricNameMap = new Map([
['activity', 'activity'],
['openrank', 'openrank'],
['participant', 'participants'],
['contributor', 'new_contributors'],
['forks', 'technical_fork'],
['stars', 'stars'],
['issues_opened', 'issues_new'],
Expand Down Expand Up @@ -33,6 +34,10 @@ export const getParticipant = async (repo: string) => {
return getMetricByName(repo, metricNameMap, 'participant');
};

export const getContributor = async (repo: string) => {
return getMetricByName(repo, metricNameMap, 'contributor');
};

export const getForks = async (repo: string) => {
return getMetricByName(repo, metricNameMap, 'forks');
};
Expand Down
3 changes: 3 additions & 0 deletions src/locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,9 @@
"header_label_participant": {
"message": "Participants"
},
"header_label_contributor": {
"message": "Contributors"
},
"fork_popup_title": {
"message": "Fork Events"
},
Expand Down
3 changes: 3 additions & 0 deletions src/locales/zh_CN/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,9 @@
"header_label_participant": {
"message": "参与人数"
},
"header_label_contributor": {
"message": "贡献人数"
},
"fork_popup_title": {
"message": "Fork 事件"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react';

const DataNotFound = () => {
return (
<div
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
color: 'var(--color-fg-muted)',
}}
>
<h3>Data Not Found</h3>
<div style={{ width: '300px', margin: '2em' }}>
<p>Possible reasons are:</p>
<ul style={{ marginLeft: '1em' }}>
<li>This repository is too new, so its data has not been generated</li>
<li>This repository is not active enough, so its data are not generated</li>
</ul>
</div>
</div>
);
};

export default DataNotFound;
78 changes: 78 additions & 0 deletions src/pages/ContentScripts/features/contributor_button/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* 该模块负责在GitHub仓库页面中,增强贡献者列表的功能。
* 它使用React来渲染贡献者列表,并通过API获取额外的网络数据。
*/

import React, { useState, useEffect } from 'react';
import { render } from 'react-dom';
import $ from 'jquery';
import View from './view';
import { getRepoName } from '../../../../helpers/get-repo-info';
import {getDeveloperNetwork, getRepoNetwork} from "../../../../api/repo";
import features from '../../../../feature-manager';
import * as pageDetect from 'github-url-detection';
import elementReady from "element-ready";

// 全局变量用于存储仓库名称和网络数据,以便在不同函数间共享
// 定义全局变量,用于存储仓库名称和网络数据。
let repoName: string;
let developerNetworks: any;
let repoNetworks: any;
let target: any;

// 获取当前模块的特征ID,用于特性管理。
const featureId = features.getFeatureID(import.meta.url);

/**
* 异步获取仓库开发者和仓库的网络数据。
*/
const getData = async () => {
developerNetworks = await getDeveloperNetwork(repoName);
repoNetworks = await getRepoNetwork(repoName);
};

/**
* 替换贡献者列表为React组件。
* @param target 要替换的目标元素。
*/
const replaceContributorList = (target: HTMLElement) => {
const originalHTML = target.innerHTML;

render(
<React.Fragment>
<View developerNetwork={developerNetworks} target={originalHTML} />
</React.Fragment>,
document.querySelector('.list-style-none.d-flex.flex-wrap.mb-n2') as HTMLElement
);
};

/**
* 初始化功能,包括获取仓库名称和数据,以及替换贡献者列表。
*/
const init = async (): Promise<void> => {
repoName = getRepoName();
const targetElement = document.querySelector('.list-style-none.d-flex.flex-wrap.mb-n2') as HTMLElement;
await getData();
replaceContributorList(targetElement);
};

/**
* 在页面刷新或导航时恢复功能,重新加载数据和渲染列表。
*/
const restore = async () => {
if (repoName !== getRepoName()) {
repoName = getRepoName();
await getData();
}
$('div.ReactModalPortal').remove();
replaceContributorList(target);
};


// 将功能添加到特性管理器中,配置初始化和恢复函数。
features.add(featureId, {
// asLongAs: [pageDetect.isUserProfile],
awaitDomReady: false,
init,
restore,
});
64 changes: 64 additions & 0 deletions src/pages/ContentScripts/features/contributor_button/view.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React, { useState, useEffect } from 'react';
import ReactModal from 'react-modal';
import Graph from '../../../../components/Graph';
import optionsStorage, { HypercrxOptions, defaults } from '../../../../options-storage';
import { useTranslation } from 'react-i18next';
import '../../../../helpers/i18n';

// 定义开发者和仓库的时间周期
const DEVELOPER_PERIOD = 90;
const REPO_PERIOD = 90;

// 定义Props接口,包括开发者网络和目标HTML
interface Props {
developerNetwork: any;
target: any;
}

// 定义图表样式
const graphStyle = {
width: '296px',
height: '400px',
};

// 定义View组件
const View = ({ developerNetwork, target}: Props): JSX.Element => {
// 定义状态变量,包括选项、是否显示图表和是否显示仓库网络
const [options, setOptions] = useState<HypercrxOptions>(defaults);
const [showGraph, setShowGraph] = useState(true);
const [showRepoNetwork, setShowRepoNetwork] = useState(false);

// 使用翻译函数
const { t, i18n } = useTranslation();

// 使用useEffect钩子来处理副作用,包括获取选项和改变语言
useEffect(() => {
(async function () {
setOptions(await optionsStorage.getAll());
i18n.changeLanguage(options.locale);
})();
}, [options.locale]);

// 返回JSX元素,包括一个按钮和一个条件渲染的图表或目标HTML
return (
<div>
<button onClick={() => setShowGraph(!showGraph)}>
切换视图
</button>
{showGraph ? (
<div className="hypertrons-crx-border hypertrons-crx-container">
<div className="d-flex flex-wrap flex-items-center" style={{ margin: '0 0 0 0', padding: "0"}}>
<div style={{ margin: '0 0 0 0', padding: "0", display: "block"}}>
<Graph data={developerNetwork} style={graphStyle} />
</div>
</div>
</div>
) : (
<div dangerouslySetInnerHTML={{ __html: target }} style={{margin: '0', padding: "0"}} />
)}
</div>
);
};

// 导出View组件
export default View;
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import React, { useEffect, useRef } from 'react';
import * as echarts from 'echarts';

import { formatNum, numberWithCommas } from '../../../../helpers/formatter';

const LIGHT_THEME = {
FG_COLOR: '#24292F',
BG_COLOR: '#ffffff',
SPLIT_LINE_COLOR: '#D0D7DE',
BAR_COLOR: '#3E90F1',
LINE_COLOR: '#267FE8',
};

const DARK_THEME = {
FG_COLOR: '#c9d1d9',
BG_COLOR: '#0d1118',
SPLIT_LINE_COLOR: '#30363D',
BAR_COLOR: '#3E90F1',
LINE_COLOR: '#82BBFF',
};

interface ContributorChartProps {
theme: 'light' | 'dark';
width: number;
height: number;
data: [string, number][];
}

const ContributorChart = (props: ContributorChartProps): JSX.Element => {
const { theme, width, height, data } = props;

const divEL = useRef(null);

const TH = theme == 'light' ? LIGHT_THEME : DARK_THEME;

const option: echarts.EChartsOption = {
tooltip: {
trigger: 'axis',
textStyle: {
color: TH.FG_COLOR,
},
backgroundColor: TH.BG_COLOR,
formatter: tooltipFormatter,
},
grid: {
top: '10%',
bottom: '5%',
left: '8%',
right: '5%',
containLabel: true,
},
xAxis: {
type: 'time',
// 30 * 3600 * 24 * 1000 milliseconds
minInterval: 2592000000,
splitLine: {
show: false,
},
axisLabel: {
color: TH.FG_COLOR,
formatter: {
year: '{yearStyle|{yy}}',
month: '{MMM}',
},
rich: {
yearStyle: {
fontWeight: 'bold',
},
},
},
},
yAxis: [
{
type: 'value',
position: 'left',
axisLabel: {
color: TH.FG_COLOR,
formatter: formatNum,
},
splitLine: {
lineStyle: {
color: TH.SPLIT_LINE_COLOR,
},
},
},
],
dataZoom: [
{
type: 'inside',
start: 0,
end: 100,
minValueSpan: 3600 * 24 * 1000 * 180,
},
],
series: [
{
type: 'bar',
data: data,
itemStyle: {
color: '#ff8061',
},
emphasis: {
focus: 'series',
},
yAxisIndex: 0,
},
{
type: 'line',
symbol: 'none',
lineStyle: {
color: '#ff8061',
},
data: data,
emphasis: {
focus: 'series',
},
yAxisIndex: 0,
},
],
animationEasing: 'elasticOut',
animationDelayUpdate: function (idx: any) {
return idx * 5;
},
};

useEffect(() => {
let chartDOM = divEL.current;
const instance = echarts.init(chartDOM as any);

return () => {
instance.dispose();
};
}, []);

useEffect(() => {
let chartDOM = divEL.current;
const instance = echarts.getInstanceByDom(chartDOM as any);
if (instance) {
instance.setOption(option);
}
}, []);

return <div ref={divEL} style={{ width, height }}></div>;
};

const tooltipFormatter = (params: any) => {
const res = `
${params[0].data[0]}<br/>
${params[0].marker}
<span style="font-weight:bold;">
${numberWithCommas(params[0].data[1])}
</span>
`;
return res;
};

export default ContributorChart;
Loading

0 comments on commit f14faf9

Please sign in to comment.