Skip to content

Commit

Permalink
App: Allow rendering all open issues on a GitHub repo
Browse files Browse the repository at this point in the history
As Juan requested [1].

[1]: jbenet#6
  • Loading branch information
wking committed Nov 28, 2016
1 parent 469dc18 commit d730109
Show file tree
Hide file tree
Showing 3 changed files with 127 additions and 54 deletions.
35 changes: 31 additions & 4 deletions webapp/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,51 @@ import { Router, IndexRoute, Route, hashHistory } from 'react-router'
import './App.css';
import DepGraph from './DepGraph';
import GetDummyHostNode, { CanonicalDummyHostKey } from './DummyHost';
import GetGitHubNode, { CanonicalGitHubKey } from './GitHub';
import GetGitHubNode, { CanonicalGitHubKey, GetGitHubRepoNodes } from './GitHub';
import GetNode, { Canonicalizers, Getters, CanonicalKey } from './GetNode';
import Home from './Home';
import Layout, { HeaderHeight } from './Layout';

export class DepGraphView extends Component {
render() {
var attributes = {};
const key = this.props.params.splat;
const host = key.split('/', 1)[0];
if (host === 'github.com') {
var match = /^github.com\/([^\/#]+)\/([^\/#]+)?$/.exec(key);
if (match === null) {
attributes.slugs = [key];
} else {
var user = match[1];
var repo = match[2];
attributes.getInitialNodes = function (pushNodes) {
return GetGitHubRepoNodes(user, repo, pushNodes);
};
}
} else {
attributes.slugs = [key];
}
return <DepGraph
width={window.innerWidth}
height={window.innerHeight - HeaderHeight}
slugs={[this.props.params.splat]}
getNode={GetNode} canonicalKey={CanonicalKey} />
getNode={GetNode} canonicalKey={CanonicalKey}
{...attributes} />
}
}

function enterGraphView(nextState, replace) {
const splat = nextState.params.splat;
const canonicalKey = CanonicalKey(splat);
var canonicalKey;
try {
canonicalKey = CanonicalKey(splat);
} catch (error) {
var match;
match = /^github.com\/([^\/#]+)\/([^\/#]+)?$/.exec(splat);
if (match === null) {
throw error;
}
canonicalKey = splat;
}
const canonicalPath = canonicalKey.replace(/#/g, '/');
if (splat === canonicalKey || splat === canonicalPath) {
return;
Expand Down
57 changes: 40 additions & 17 deletions webapp/src/DepGraph.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ class DepGraph extends PureComponent {

componentDidMount() {
this.nodes();
if (this.props.getInitialNodes) {
this.props.getInitialNodes(this.pushNodes.bind(this));
}
}

nodes() {
Expand All @@ -22,38 +25,54 @@ class DepGraph extends PureComponent {
if (!this.props.canonicalKey) {
throw new Error('canonicalKey unset');
}
for (var index in this.props.slugs) {
for (var index in (this.props.slugs || [])) {
if (true) {
var key = this.props.slugs[index];
this.getNode(key);
}
}
}

getNode(key) {
pushNodes(nodes) {
var index, node;
var _this = this;
key = this.props.canonicalKey(key);
this.setState(function (prevState) {
if (prevState.nodes[key] || prevState.pending[key]) {
return prevState;
var stateNodes = {...prevState.nodes};
var pending = {...prevState.pending};
for (index in nodes) {
if (true) {
var node = nodes[index];
stateNodes[node.props.slug] = node;
delete pending[node.props.slug];
}
}
_this.props.getNode(key).then(function (node) {
_this.setState(function (prevState) {
var nodes = {...prevState.nodes};
nodes[key] = node;
var pending = {...prevState.pending};
delete pending[key];
return {...prevState, nodes: nodes, pending: pending};
});
return {...prevState, nodes: stateNodes, pending: pending};
});
for (index in nodes) {
if (true) {
node = nodes[index];
if (!node.props.done) {
var parents = node.parents();
for (var index in parents) {
for (var i in parents) {
if (true) {
var relatedKey = parents[index];
_this.getNode(relatedKey);
var relatedKey = parents[i];
this.getNode(relatedKey);
}
}
}
}
}
}

getNode(key) {
var _this = this;
key = this.props.canonicalKey(key);
this.setState(function (prevState) {
if (prevState.nodes[key] || prevState.pending[key]) {
return prevState;
}
_this.props.getNode(key).then(function (node) {
_this.pushNodes([node]);
});
var pending = {...prevState.pending};
pending[key] = true;
Expand All @@ -63,8 +82,12 @@ class DepGraph extends PureComponent {

/* Properties:
*
* * slugs, roots for the issue graph. An array of strings, like:
* * slugs, (optional) roots for the issue graph. An array of
* strings, like:
* ['github.com/jbenet/depviz#1', 'gitlab.com/foo/bar#123']
* * getInitialNodes(pushNodes), (optional) a callback for resolving
* initial nodes. pushNodes takes an array of nodes and may be
* called multiple times.
* * width, the width of the graph viewport in pixels.
* * height, the height of the graph viewport in pixels.
* * canonicalKey(key) -> key, a callback for canonicalizing node
Expand Down
89 changes: 56 additions & 33 deletions webapp/src/GitHub.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,51 @@ export function CanonicalGitHubKey(key) {
return 'github.com/' + user + '/' + repo + '#' + number;
}

function nodeFromIssue(issue) {
var dependencies = [];
var related = [];
var regexp = /^depends +on +(?:([^ ]+)\/)?(?:([^ ]+))?(?:#([0-9]+)) *$/gim;
// FIXME: look for related too
var match;
match = /^.*\/([^\/]+)\/([^\/]+)$/.exec(issue.repository_url);
if (match === null) {
throw new Error(
'unrecognized repository URL format: ' + issue.repository_url
);
}
var issueUser = match[1];
var issueRepo = match[2];
var key = 'github.com/' + issueUser + '/' + issueRepo + '#' + issue.number;
for (;;) {
match = regexp.exec(issue.body);
if (match === null) {
break;
}
var user = match[1];
var repo = match[2];
var number = parseInt(match[3], 10);
if ((user && !repo) || (!user && repo)) {
continue;
}
if (!user && !repo) {
user = issueUser;
repo = issueRepo;
}
var relatedKey = 'github.com/' + user + '/' + repo + '#' + number;
dependencies.push(relatedKey);
}
return new DepCard({
slug: key,
host: 'github.com',
title: issue.title,
href: issue.html_url,
done: issue.state !== 'open',
dependencies: dependencies,
related: related,
user: issue.user.login,
});
}

function GetGitHubNode(key) {
var match = /^github\.com\/([^\/]*)\/([^\/]*)#([0-9]*)$/.exec(key);
if (!match) {
Expand All @@ -33,40 +78,18 @@ function GetGitHubNode(key) {
).getIssue(
number
).then(function (issue) {
var dependencies = [];
var related = [];
var regexp = /^depends +on +(?:([^ ]+)\/)?(?:([^ ]+))?(?:#([0-9]+)) *$/gim;
// FIXME: look for related too
var match;
for (;;) {
match = regexp.exec(issue.data.body);
if (match === null) {
break;
}
var user1 = match[1];
var repo1 = match[2];
var number1 = parseInt(match[3], 10);
if ((user1 && !repo1) || (!user1 && repo1)) {
continue;
}
if (!user1 && !repo1) {
user1 = user;
repo1 = repo;
}
var relatedKey = 'github.com/' + user1 + '/' + repo1 + '#' + number1;
dependencies.push(relatedKey);
}
return new DepCard({
slug: key,
host: 'github.com',
title: issue.data.title,
href: issue.data.html_url,
done: issue.data.state !== 'open',
dependencies: dependencies,
related: related,
user: issue.data.user.login,
});
return nodeFromIssue(issue.data);
});
}

export function GetGitHubRepoNodes(user, repo, pushNodes) {
gh.getIssues(
user, repo
).listIssues(
).then(function (issues) {
var nodes = issues.data.map(nodeFromIssue);
pushNodes(nodes);
});
}

export default GetGitHubNode;

0 comments on commit d730109

Please sign in to comment.