From 7aedbc3934aad46a2fb1389f16c1ebdc6111a04f Mon Sep 17 00:00:00 2001 From: andyhuang18 <763230412@qq.com> Date: Wed, 14 Jun 2023 23:30:35 +0800 Subject: [PATCH] feat: add activity racing bar 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 --- src/api/repo.ts | 5 + src/locales/en/messages.json | 6 + src/locales/zh_CN/messages.json | 6 + .../repo-activity-racing-bar/RacingBar.tsx | 171 ++++++++++++++++++ .../repo-activity-racing-bar/index.tsx | 59 ++++++ .../repo-activity-racing-bar/view.tsx | 65 +++++++ src/pages/ContentScripts/index.scss | 17 ++ src/pages/ContentScripts/index.ts | 1 + 8 files changed, 330 insertions(+) create mode 100644 src/pages/ContentScripts/features/repo-activity-racing-bar/RacingBar.tsx create mode 100644 src/pages/ContentScripts/features/repo-activity-racing-bar/index.tsx create mode 100644 src/pages/ContentScripts/features/repo-activity-racing-bar/view.tsx diff --git a/src/api/repo.ts b/src/api/repo.ts index 5be9f536..63858546 100644 --- a/src/api/repo.ts +++ b/src/api/repo.ts @@ -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) => { @@ -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'); +}; diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json index 77ef9ea2..bc591c44 100644 --- a/src/locales/en/messages.json +++ b/src/locales/en/messages.json @@ -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." }, diff --git a/src/locales/zh_CN/messages.json b/src/locales/zh_CN/messages.json index 44d8076e..927407bb 100644 --- a/src/locales/zh_CN/messages.json +++ b/src/locales/zh_CN/messages.json @@ -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": "项目关系网络图展示了在给定的时间段内,项目与项目之间的联结关系,用于项目间关系的追踪与挖掘。从该网络图中,可以找出与该项目有联结关系的其他项目。" }, diff --git a/src/pages/ContentScripts/features/repo-activity-racing-bar/RacingBar.tsx b/src/pages/ContentScripts/features/repo-activity-racing-bar/RacingBar.tsx new file mode 100644 index 00000000..8629a61d --- /dev/null +++ b/src/pages/ContentScripts/features/repo-activity-racing-bar/RacingBar.tsx @@ -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 ( +
+ {getMessageByLocale( + 'component_projectRacingBar_description', + options.locale + )} +
+