Skip to content

Commit

Permalink
feat: add activity racing bar
Browse files Browse the repository at this point in the history
feat: initial implementation of activity racing bar(need to be modified)

feat: initial implementation of activity racing bar(need to be modified v1)

feat: initial implementation of activity racing bar(need to be modified v2)

feat: implementation of activity racing bar

feat: change the style of chart and button
  • Loading branch information
andyhuang18 committed Aug 10, 2023
1 parent 746d58b commit 7aedbc3
Show file tree
Hide file tree
Showing 8 changed files with 330 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/api/repo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const metricNameMap = new Map([
['merged_code_sum', 'code_change_lines_sum'],
['developer_network', 'developer_network'],
['repo_network', 'repo_network'],
['activity_details', 'activity_details'],
]);

export const getActivity = async (repo: string) => {
Expand Down Expand Up @@ -83,3 +84,7 @@ export const getDeveloperNetwork = async (repo: string) => {
export const getRepoNetwork = async (repo: string) => {
return getMetricByName(repo, metricNameMap, 'repo_network');
};

export const getActivityDetails = async (repo: string) => {
return getMetricByName(repo, metricNameMap, 'activity_details');
};
6 changes: 6 additions & 0 deletions src/locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,15 @@
"component_developerActORTrend_yName2": {
"message": "OpenRank"
},
"component_projectRacingBar_title": {
"message": "Project User Activity Racing Bar"
},
"component_projectCorrelationNetwork_title": {
"message": "Project Correlation Network"
},
"component_projectRacingBar_description": {
"message": "Project User Activity Racing Bar Shows the change of user activity, which is used to discover the trend of user participation in projects."
},
"component_projectCorrelationNetwork_description": {
"message": "Project Correlation Network shows the correlation between projects for a given time period. From this graph you can find the projects that are related to the given project."
},
Expand Down
6 changes: 6 additions & 0 deletions src/locales/zh_CN/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,15 @@
"component_developerActORTrend_yName2": {
"message": "OpenRank"
},
"component_projectRacingBar_title": {
"message": "项目用户活跃度对比图"
},
"component_projectCorrelationNetwork_title": {
"message": "项目关系网络图"
},
"component_projectRacingBar_description": {
"message": "项目用户活跃度对比图展示了用户活跃度的变化,用于发现用户参与项目变化趋势。"
},
"component_projectCorrelationNetwork_description": {
"message": "项目关系网络图展示了在给定的时间段内,项目与项目之间的联结关系,用于项目间关系的追踪与挖掘。从该网络图中,可以找出与该项目有联结关系的其他项目。"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import React, { useState, useEffect, useRef } from 'react';
import * as echarts from 'echarts';

interface RacingBarProps {
//theme: 'light' | 'dark';
width: number;
height: number;
repoName: string;
data: any;
}

const RacingBar = (props: RacingBarProps): JSX.Element => {
const [playing, setPlaying] = useState(0);
const { width, height, data } = props;
const divEL = useRef(null);
const updateFrequency = 3000;
const colorMap = new Map();
const option = {
grid: {
top: 10,
bottom: 30,
left: 150,
right: 80,
},
xAxis: {
max: 'dataMax',
},
yAxis: {
type: 'category',
inverse: true,
max: 10,
axisLabel: {
show: true,
fontSize: 14,
formatter: function (value: string) {
if (!value || value.endsWith('[bot]')) return value;
return `${value} {avatar${value.replaceAll('-', '')}|}`;
},
rich: null,
},
animationDuration: 300,
animationDurationUpdate: 300,
},
series: [
{
realtimeSort: true,
seriesLayoutBy: 'column',
type: 'bar',
itemStyle: {
color: function (params: { value: any[] }) {
const githubId = params.value[0];
if (colorMap.has(githubId)) {
return colorMap.get(githubId);
} else {
const randomColor =
'#' + Math.floor(Math.random() * 16777215).toString(16);
colorMap.set(githubId, randomColor);
return randomColor;
}
},
},
data: null,
encode: {
x: 1,
y: 0,
},
label: {
show: true,
precision: 1,
position: 'right',
valueAnimation: true,
fontFamily: 'monospace',
},
},
],
// Disable init animation.
animationDuration: 0,
animationDurationUpdate: updateFrequency,
animationEasing: 'linear',
animationEasingUpdate: 'linear',
graphic: {
elements: [
{
type: 'text',
right: 60,
bottom: 60,
style: {
text: null,
font: 'bolder 60px monospace',
fill: 'rgba(100, 100, 100, 0.25)',
},
z: 100,
},
],
},
};

useEffect(() => {
// @ts-ignore
let chartDOM = divEL.current;
const instance = echarts.init(chartDOM as any);
// 组件卸载时销毁图表
return () => {
instance.dispose();
};
}, [playing]);

useEffect(() => {
let chartDOM = divEL.current;
const instance = echarts.getInstanceByDom(chartDOM as any);
const months = Object.keys(data);
// 在数据变化时调用图表更新函数
// 根据传入的新数据进行图表的更新操作
let startIndex = 0;

for (let i = startIndex; i < months.length - 1; ++i) {
(function (i) {
setTimeout(function () {
updateMonth(months[i + 1]);
if (i + 1 === months.length - 1) {
}
}, (i - startIndex) * updateFrequency);
})(i);
}

// @ts-ignore
function updateMonth(month: string | null) {
const rich = {};
// @ts-ignore
data[month].forEach((item: any[]) => {
// rich name cannot contain special characters such as '-'
// @ts-ignore
rich[`avatar${item[0].replaceAll('-', '')}`] = {
backgroundColor: {
image: `https://avatars.githubusercontent.com/${item[0]}?s=48&v=4`,
},
height: 20,
};
});
// @ts-ignore
option.yAxis.axisLabel.rich = rich;
// @ts-ignore
option.series[0].data = data[month];
// @ts-ignore
option.graphic.elements[0].style.text = month;
// @ts-ignore
instance.setOption(option);
}
}, [playing]);

const handleReplayClick = () => {
let chartDOM = divEL.current;
const instance = echarts.getInstanceByDom(chartDOM as any);
// @ts-ignore
instance.setOption(option);
setPlaying(playing + 1);
};

return (
<div className="hypertrons-crx-border">
<div ref={divEL} style={{ width, height }}></div>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<button className="replay-button" onClick={handleReplayClick}>
Replay
</button>
</div>
</div>
);
};

export default RacingBar;
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import React from 'react';
import { render, Container } from 'react-dom';
import $ from 'jquery';

import features from '../../../../feature-manager';
import isPerceptor from '../../../../helpers/is-perceptor';
import { getRepoName } from '../../../../helpers/get-repo-info';
import { getActivityDetails } from '../../../../api/repo';
import View from './view';
import DataNotFound from '../repo-networks/DataNotFound';
import * as pageDetect from 'github-url-detection';

const featureId = features.getFeatureID(import.meta.url);
let repoName: string;
let repoActivityDetails: any;

const getData = async () => {
repoActivityDetails = await getActivityDetails(repoName);
};

const renderTo = (container: Container) => {
if (!repoActivityDetails) {
render(<DataNotFound />, container);
return;
}
render(
<View currentRepo={repoName} repoActivityDetails={repoActivityDetails} />,
container
);
};

const init = async (): Promise<void> => {
repoName = getRepoName();
await getData();
const container = document.createElement('div');
container.id = featureId;
renderTo(container);
const parentElement = document.getElementById('hypercrx-perceptor-layout');
if (parentElement) {
parentElement.append(container);
}
};

const restore = async () => {
// Clicking another repo link in one repo will trigger a turbo:visit,
// so in a restoration visit we should be careful of the current repo.
if (repoName !== getRepoName()) {
repoName = getRepoName();
}
// rerender the chart or it will be empty
renderTo($(`#${featureId}`)[0]);
};

features.add(featureId, {
asLongAs: [isPerceptor],
awaitDomReady: false,
init,
restore,
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import React, { useState, useEffect } from 'react';

import getMessageByLocale from '../../../../helpers/get-message-by-locale';
import optionsStorage, {
HypercrxOptions,
defaults,
} from '../../../../options-storage';
import RacingBar from './RacingBar';

interface Props {
currentRepo: string;
repoActivityDetails: any;
}

const View = ({ currentRepo, repoActivityDetails }: Props): JSX.Element => {
const [options, setOptions] = useState<HypercrxOptions>(defaults);

useEffect(() => {
(async function () {
setOptions(await optionsStorage.getAll());
})();
}, []);

return (
<div>
<div className="hypertrons-crx-border hypertrons-crx-container">
<div className="hypertrons-crx-title">
<span>
{getMessageByLocale(
'component_projectRacingBar_title',
options.locale
)}
</span>
</div>
<div className="d-flex flex-wrap flex-items-center">
<div className="col-12 col-md-8">
<div style={{ margin: '10px 0 20px 20px' }}>
<RacingBar
repoName={currentRepo}
height={300}
width={700}
data={repoActivityDetails}
/>
</div>
</div>
<div className="col-12 col-md-4">
<div
className="color-text-secondary"
style={{ marginLeft: '35px', marginRight: '35px' }}
>
<p>
{getMessageByLocale(
'component_projectRacingBar_description',
options.locale
)}
</p>
</div>
</div>
</div>
</div>
</div>
);
};

export default View;
17 changes: 17 additions & 0 deletions src/pages/ContentScripts/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,20 @@
-ms-user-select: none; /* IE10+/Edge */
user-select: none; /* Standard */
}

.replay-button {
display: inline-block;
padding: 5px 10px;
font-size: 8px;
font-weight: bold;
text-align: center;
text-decoration: none;
border-radius: 4px;
cursor: pointer;
background-color: #4caf50;
color: white;
border: none;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
transition: background-color 0.3s, transform 0.2s, box-shadow 0.3s;
margin-bottom: 2px;
}
1 change: 1 addition & 0 deletions src/pages/ContentScripts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ import './features/perceptor-layout';
import './features/repo-networks';
import './features/developer-networks';
import './features/oss-gpt';
import './features/repo-activity-racing-bar';

0 comments on commit 7aedbc3

Please sign in to comment.