diff --git a/src/App.js b/src/App.js index 5182f18..f5df433 100644 --- a/src/App.js +++ b/src/App.js @@ -1,7 +1,6 @@ import React from "react"; -import { makeStyles, Paper, IconButton, Snackbar } from "@material-ui/core"; +import { makeStyles, Paper, Snackbar } from "@material-ui/core"; import TextField from "@material-ui/core/TextField"; -import SearchIcon from "@material-ui/icons/Search"; import MuiAlert from "@material-ui/lab/Alert"; import Autocomplete from "@material-ui/lab/Autocomplete"; import Tabs from "@material-ui/core/Tabs"; @@ -9,18 +8,20 @@ import Tab from "@material-ui/core/Tab"; import PropTypes from "prop-types"; import Typography from "@material-ui/core/Typography"; import Box from "@material-ui/core/Box"; -import Button from "@material-ui/core/Button"; +import IconButton from "@material-ui/core/IconButton"; +import InputAdornment from "@material-ui/core/InputAdornment"; +import SearchIcon from "@material-ui/icons/Search"; +import cloneDeep from "lodash.clonedeep"; import ContirbutorLineChart from "./components/contributor"; import ActivityChart from "./components/activity"; import { getParameterByName } from "./utils"; +import CompareComponent from "./components/compare"; const Alert = props => { return ; }; -const ALLOW_MERGE_LIST = ["skywalking", "apisix"]; - const useStyles = makeStyles(theme => ({ button: { margin: theme.spacing(1) @@ -37,11 +38,11 @@ const useStyles = makeStyles(theme => ({ display: "flex", flexWrap: "wrap", alignItems: "center", - width: 600 + width: "100%" }, autocomplete: { - marginLeft: theme.spacing(1), - flex: 1 + flex: 1, + paddingTop: "5px" }, iconButton: { padding: 10 @@ -85,14 +86,6 @@ function a11yProps(index) { }; } -const useTabStyles = makeStyles(theme => ({ - root: { - flexGrow: 1, - width: "100%", - backgroundColor: theme.palette.background.paper - } -})); - const App = () => { const [repo, setRepo] = React.useState("apache/apisix"); const [message, setMessage] = React.useState(""); @@ -100,12 +93,8 @@ const App = () => { const [alertType, setAlertType] = React.useState("success"); const [searchOption, setSearchOption] = React.useState([]); const [contributorRepoList, setContributorRepoList] = React.useState([]); - const classesTable = useTabStyles(); const [value, setValue] = React.useState(0); const [tabdisabled, setTabDisabled] = React.useState(false); - const [showMergeButton, setShowMergeButton] = React.useState(false); - const [mergeStatus, setMergeStatus] = React.useState(false); - const [mergeRepo, setMergeRepo] = React.useState("apache/apisix"); const handleChange = (event, newValue) => { setValue(newValue); @@ -128,10 +117,6 @@ const App = () => { }; const updateChart = repo => { - const index = ALLOW_MERGE_LIST.findIndex(item => repo.includes(item)); - if (index === -1) { - setMergeStatus(false); - } if (!contributorRepoList.includes(repo)) { setContributorRepoList([...contributorRepoList, repo]); } @@ -158,25 +143,14 @@ const App = () => { React.useEffect(() => { getSearchOptions(); const repo = getParameterByName("repo") || "apache/apisix"; + const repoArr = repo.split(",").filter(Boolean); + setContributorRepoList(repoArr); + const chart = getParameterByName("chart"); if (chart === "contributorMonthlyActivity") { setValue(1); } else { - const merge = getParameterByName("merge"); - setRepo(repo); - const index = ALLOW_MERGE_LIST.findIndex(item => repo.includes(item)); - if (merge === "true" && index !== -1) { - setTimeout(() => { - setMergeStatus(true); - setShowMergeButton(true); - }, 500); - } - } - if (repo) { - const repoArr = repo.split(",").filter(Boolean); - setContributorRepoList(repoArr); - } else { - setContributorRepoList(["apache/apisix"]); + setValue(0); } }, []); @@ -190,26 +164,6 @@ const App = () => { ); }, [value]); - React.useEffect(() => { - const index = ALLOW_MERGE_LIST.findIndex(item => repo.includes(item)); - if (index !== -1) { - setShowMergeButton(true); - } else { - setShowMergeButton(false); - setMergeStatus(false); - } - if (contributorRepoList.length === 0) { - setMergeStatus(false); - setShowMergeButton(false); - } - if (repo.includes("skywalking")) { - setMergeRepo("apache/skywalking"); - } - if (repo.includes("apisix")) { - setMergeRepo("apache/apisix"); - } - }, [repo, contributorRepoList]); - return ( <> {
-
+
+ + { + if (reason === "reset") { + setRepo(value); + updateChart(value); + } + }} + renderInput={params => ( + { + setRepo(e.target.value); + }} + onKeyPress={ev => { + if (ev.key === "Enter") { + updateChart(repo); + ev.preventDefault(); + } + }} + InputProps={{ + ...params.InputProps, + endAdornment: ( + + { + updateChart(repo); + }} + > + + + + ) + }} + /> + )} + /> + +
+ { + const clonedContributorRepoList = cloneDeep( + contributorRepoList + ); + const newContributorRepoList = clonedContributorRepoList.filter( + item => item !== e + ); + setContributorRepoList(newContributorRepoList); + }} + /> +
+
+
{ justifyContent: "center", flexDirection: "column" }} - > - - { - if (reason === "reset") { - setRepo(value); - updateChart(value); - } - }} - renderInput={params => ( - { - setRepo(e.target.value); - }} - onKeyPress={ev => { - if (ev.key === "Enter") { - updateChart(repo); - ev.preventDefault(); - } - }} - InputProps={{ ...params.InputProps, type: "search" }} - /> - )} - /> - { - updateChart(repo); - }} - > - - - - {Boolean(!value) && Boolean(showMergeButton) && ( - - )} -
+ >
-
+
@@ -340,17 +314,11 @@ const App = () => { { setTabDisabled(e); }} onDelete={e => { - if (mergeStatus) { - setMergeStatus(false); - return; - } setContributorRepoList( contributorRepoList.filter(item => item !== e) ); diff --git a/src/components/activity/index.js b/src/components/activity/index.js index 3f62918..64dafe7 100644 --- a/src/components/activity/index.js +++ b/src/components/activity/index.js @@ -5,7 +5,6 @@ import * as echarts from "echarts"; import ReactECharts from "echarts-for-react"; import omit from "lodash.omit"; -import CompareComponent from "../compare"; import { DEFAULT_ACTIVITY_OPTIONS, DEFAULT_COLOR } from "../../constants"; const ActivityChart = ({ @@ -171,29 +170,6 @@ const ActivityChart = ({ }); }; - const updateChart = repo => { - if (dataSource[repo]) return; - setLoading(true); - fetchData(repo) - .then(myJson => { - const { Contributors = [] } = myJson; - const data = Contributors.map(item => ({ - repo, - contributorNum: item.Num, - date: item.Month - })); - - const clonedDatasource = cloneDeep(dataSource); - if (!clonedDatasource[repo]) { - setDataSource({ ...clonedDatasource, ...{ [repo]: data } }); - } - setLoading(false); - }) - .catch(() => { - setLoading(false); - }); - }; - React.useEffect(() => { updateSeries(xAxis); window.parent.postMessage({ legend: Object.keys(dataSource) }, "*"); @@ -254,22 +230,7 @@ const ActivityChart = ({ }} >
-
- { - const clonedDataSource = cloneDeep(dataSource); - const newDataSource = omit(clonedDataSource, [e]); - setDataSource(newDataSource); - onDelete(e); - }} - onConfirm={e => { - if (!e) return; - updateChart(e); - }} - /> -
-
+
@@ -284,7 +245,7 @@ const ActivityChart = ({ window.echartInstance = echartInstance; } }} - style={{ height: 700, width: "100%" }} + style={{ height: 550 }} showLoading={loading} notMerge /> diff --git a/src/components/chip.js b/src/components/chip.js index ff7a3b0..c1544aa 100644 --- a/src/components/chip.js +++ b/src/components/chip.js @@ -10,7 +10,7 @@ const useStyles = makeStyles(theme => ({ "& > *": { margin: theme.spacing(0.5) } - } + }, })); export default function OutlinedChips({ list = [], onDelete }) { @@ -23,6 +23,7 @@ export default function OutlinedChips({ list = [], onDelete }) { icon={} label={item} key={item} + size="small" onDelete={() => onDelete(item)} color="primary" variant="outlined" diff --git a/src/components/compare.js b/src/components/compare.js index 340b6a3..ef9c763 100644 --- a/src/components/compare.js +++ b/src/components/compare.js @@ -10,7 +10,7 @@ const useStyles = makeStyles(theme => ({ flexWrap: "wrap", flexGrow: 1, "& > *": { - margin: theme.spacing(1), + margin: '0 0 8px 0', width: "100%", height: theme.spacing(8) } diff --git a/src/components/contributor/index.js b/src/components/contributor/index.js index 7c07981..f2c56c4 100644 --- a/src/components/contributor/index.js +++ b/src/components/contributor/index.js @@ -1,23 +1,23 @@ import React from "react"; import cloneDeep from "lodash.clonedeep"; -import { Row, Col, Tab } from "react-bootstrap"; import ReactECharts from "echarts-for-react"; +import * as echarts from "echarts"; import omit from "lodash.omit"; -import CompareComponent from "../../components/compare"; import { Button, ButtonGroup } from "@material-ui/core"; -import { getMonths } from "../../utils"; +import { getMonths, getParameterByName } from "../../utils"; import { generateDefaultOption } from "../../constants"; import { fetchData, fetchMergeContributor } from "./service"; import CustomizedDialogs from "../shareDialog"; +import { DEFAULT_COLOR } from "../../constants"; +import SyntaxHighlighter from "react-syntax-highlighter/dist/esm/default-highlight"; +import { a11yDark } from "react-syntax-highlighter/dist/esm/styles/hljs"; const ContributorLineChart = ({ - repoList = ["apache/apisix"], + repoList = [], showAlert, onDelete, - onLoading, - isMerge = false, - mergeRepo = "" + onLoading }) => { const [loading, setLoading] = React.useState(false); const [dataSource, setDataSource] = React.useState({}); @@ -32,8 +32,52 @@ const ContributorLineChart = ({ }) ); + const [viewMerge, setViewMerge] = React.useState(false); + const [mergeRepo, setMergerRepo] = React.useState(""); + + const showMergeButton = React.useMemo(() => { + const lastItem = repoList[repoList.length - 1]; + return lastItem === "apache/apisix" || lastItem === "apache/skywalking"; + }, [repoList]); + + const SHARE_BASE_URL = "https://www.apiseven.com/en/contributor-graph"; + const IMG_BASE_URL = + "https://contributor-graph-api.apiseven.com/contributors-svg"; + + const MarkdownLink = ({ params = "" }) => { + return ( +
+

+ You can include the chart on your repository's README.md as follows: +

+ + {` + ## Contributor over time + + [![Contributor over time](${IMG_BASE_URL + params})[${SHARE_BASE_URL + + params}]]`} + +
+ ); + }; + + React.useEffect(() => { + if (showMergeButton) { + setMergerRepo(repoList[repoList.length - 1]); + return; + } + setMergerRepo(""); + }, [repoList, showMergeButton]); + + React.useEffect(() => { + // reset viewmerge when repo list change + if (!showMergeButton) { + setViewMerge(false); + } + }, [repoList.length]); + const getShareParams = () => { - if (isMerge) { + if (viewMerge) { return `?chart=contributorOverTime&repo=${mergeRepo}&merge=true`; } return `?chart=contributorOverTime&repo=${repoList.join(",")}`; @@ -108,6 +152,29 @@ const ContributorLineChart = ({ } })); + if (series.length === 1) { + series[0].areaStyle = { + color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + { + offset: 0, + color: DEFAULT_COLOR + "80" + }, + { + offset: 1, + color: DEFAULT_COLOR + "00" + } + ]) + }; + series[0].itemStyle = { + normal: { + color: DEFAULT_COLOR, + lineStyle: { + color: DEFAULT_COLOR + } + } + }; + } + newClonedOption.dataset = [ { id: "dataset_raw", @@ -169,7 +236,7 @@ const ContributorLineChart = ({ return; } - if (!isMerge) { + if (!viewMerge) { setLoading(true); Promise.all(repoList.map(item => fetchData(item, showAlert, onDelete))) .then(data => { @@ -196,28 +263,40 @@ const ContributorLineChart = ({ } else { if (!mergeRepo.length) return; setLoading(true); - fetchMergeContributor(mergeRepo, showAlert, onDelete) + fetchMergeContributor([mergeRepo], showAlert, onDelete) .then(_data => { const tmpDataSouce = {}; const { Contributors = [], repo } = _data; const data = Contributors.map(item => ({ - repo, + repo: repo.join(","), contributorNum: item.idx, date: item.date })); - if (!tmpDataSouce[_data.repo]) { - tmpDataSouce[repo] = data; + if (!tmpDataSouce[repo.join(",")]) { + tmpDataSouce[repo.join(",")] = data; } setDataSource(tmpDataSouce); setLoading(false); }) - .catch(() => { + .catch(e => { setLoading(false); }); } - }, [repoList, isMerge]); + }, [repoList, viewMerge]); + + React.useEffect(() => { + const merge = getParameterByName("merge"); + const repo = getParameterByName("repo"); + if ( + (merge === "true" && repo === "apache/apisix") || + repo === "apache/skywalking" + ) { + setMergerRepo(repo); + setViewMerge(true); + } + }, []); return ( <> @@ -229,105 +308,98 @@ const ContributorLineChart = ({ }} > -
-
- { - const clonedDataSource = cloneDeep(dataSource); - const newDataSource = omit(clonedDataSource, [e]); - setDataSource(newDataSource); - onDelete(e); +
+
+
670 ? "flex" : "unset", + justifyContent: "space-between" }} + > + + + + + + + + + {showMergeButton && ( + + )} +
+ { + if (e) { + const echartInstance = e.getEchartsInstance(); + // then you can use any API of echarts. + window.echartInstance = echartInstance; + } + }} + style={{ height: 550 }} + showLoading={loading} + notMerge /> -
-
- - - - - -
- - - - - - - -
- { - if (e) { - const echartInstance = e.getEchartsInstance(); - // then you can use any API of echarts. - window.echartInstance = echartInstance; - } - }} - style={{ height: 700, width: "100%" }} - showLoading={loading} - notMerge - /> -
-
- -
-
+
diff --git a/src/components/contributor/service.js b/src/components/contributor/service.js index 86ad0bd..4e09674 100644 --- a/src/components/contributor/service.js +++ b/src/components/contributor/service.js @@ -1,6 +1,5 @@ import moment from "moment"; import { isSameDay } from "../../utils"; -import { getGithubRepoList } from "../../api/service"; export const fetchData = (repo, showAlert, onDelete) => { if (repo === "null" || repo === null) { @@ -89,37 +88,36 @@ export const fetchData = (repo, showAlert, onDelete) => { export const fetchMergeContributor = (repo, showAlert) => { return new Promise((resolve, reject) => { - getGithubRepoList(repo).then(data => { - fetch( - `https://contributor-graph-api.apiseven.com/contributors-multi?repo=${data.join( - "," - )}` - ) - .then(response => { - if (!response.ok) { - let message = ""; - switch (response.status) { - case 403: - message = "Hit rate limit"; - break; - case 404: - message = "Repo format error / Repo not found"; - break; - default: - message = "Request Error"; - break; - } - throw message; + fetch( + `https://contributor-graph-api.apiseven.com/contributors-multi?repo=${repo.join( + "," + )}` + ) + .then(response => { + if (!response.ok) { + let message = ""; + switch (response.status) { + case 403: + message = "Hit rate limit"; + break; + case 404: + message = "Repo format error / Repo not found"; + break; + default: + message = "Request Error"; + break; } - return response.json(); - }) - .then(myJson => { - resolve({ repo, ...myJson }); - }) - .catch(e => { - showAlert(e, "error"); - reject(); - }); - }); + throw message; + } + return response.json(); + }) + .then(myJson => { + console.log('myJson: ', myJson); + resolve({ repo, ...myJson }); + }) + .catch(e => { + showAlert(e, "error"); + reject(); + }); }); }; diff --git a/src/components/shareDialog/index.css b/src/components/shareDialog/index.css new file mode 100644 index 0000000..eaaef66 --- /dev/null +++ b/src/components/shareDialog/index.css @@ -0,0 +1,3 @@ +.shareInput .MuiOutlinedInput-root { + border-radius: 0px; +} diff --git a/src/components/shareDialog/index.js b/src/components/shareDialog/index.js index d2602ac..caf915c 100644 --- a/src/components/shareDialog/index.js +++ b/src/components/shareDialog/index.js @@ -9,13 +9,12 @@ import Typography from "@material-ui/core/Typography"; import Paper from "@material-ui/core/Paper"; import Grid from "@material-ui/core/Grid"; import { makeStyles } from "@material-ui/core/styles"; -import SyntaxHighlighter from "react-syntax-highlighter"; -import { a11yDark } from "react-syntax-highlighter/dist/esm/styles/hljs"; import Button from "@material-ui/core/Button"; import TextField from "@material-ui/core/TextField"; import copy from "copy-to-clipboard"; import { inIframe } from "../../utils"; +import "./index.css"; const styles = theme => ({ root: { @@ -43,13 +42,15 @@ const ShareLink = ({ params = "" }) => { id="standard-basic" label="Share Link" variant="outlined" + className="shareInput" value={`${SHARE_BASE_URL}${params}`} size="small" - style={{ width: "400px" }} + style={{ width: "400px", borderRadius: "0px" }} />
{
diff --git a/src/constants.js b/src/constants.js index de029e0..075e7a1 100644 --- a/src/constants.js +++ b/src/constants.js @@ -5,7 +5,7 @@ export const generateDefaultOption = ({ handleShareClick = () => {} }) => { top: "5%", data: [], textStyle: { - fontSize: 16 + fontSize: 14 } }, toolbox: { @@ -18,10 +18,6 @@ export const generateDefaultOption = ({ handleShareClick = () => {} }) => { onclick: function() { handleShareClick(); } - }, - saveAsImage: { - show: true, - type: "png" } } }, @@ -51,26 +47,26 @@ export const generateDefaultOption = ({ handleShareClick = () => {} }) => { } } }, - series: [] + series: [], + grid: { + x: 40, + x2: 15, + y: 80 + } }; }; export const DEFAULT_ACTIVITY_OPTIONS = { color: ["#39a85a", "#4385ee", "#fabc37", "#2dc1dd", "#f972cf", "#8331c8"], legend: { - top: "5%", + top: "10%", data: [], textStyle: { fontSize: 16 } }, toolbox: { - feature: { - saveAsImage: { - show: true, - type: "png" - } - } + feature: {} }, dataset: [], title: { @@ -100,7 +96,12 @@ export const DEFAULT_ACTIVITY_OPTIONS = { } } }, - series: [] + series: [], + grid: { + x: 31, + x2: 13, + y: 80 + } }; export const DEFAULT_COLOR = "#39a85a";