Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
"links": {
"title": "GitHub",
"host": "attribute",
"entryPoint": "src/links.js",
"entryPoint": "src/views/attribute.js",
"recordTypes": [
"Feature",
"Epic",
Expand All @@ -44,10 +44,15 @@
"prs": {
"title": "My Pull Requests",
"host": "page",
"entryPoint": "src/prs.js",
"entryPoint": "src/views/prsPage.js",
"location": {
"menu": "Work"
}
},
"prPanel": {
"title": "GitHub pull requests",
"entryPoint": "src/views/prPanel.tsx",
"host": "panel"
}
},
"commands": {
Expand All @@ -71,14 +76,13 @@
"endpoints": {
"webhook": {
"title": "Webhook from Github",
"entryPoint": "src/webhook.js",
"entryPoint": "src/webhooks/webhook.js",
"public": true
}
}
}
},
"devDependencies": {
"@aha-app/aha-cli": "^1.9.2",
"@types/react": "^17.0.3",
"@types/react-dom": "^17.0.3"
}
Expand Down
13 changes: 7 additions & 6 deletions src/prs.js → src/components/ExtensionRoot.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { AuthProvider } from "@aha-app/aha-develop-react";
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

git has picked this up as a file rename, but actually this file moved to src/views/prsPage.js and the ExtensionRoot is an extraction from all the react entry points.

import React from "react";
import Styles from "./components/Styles";
import { Page } from "./components/page/Page";
import { Styles } from "./Styles";

aha.on("prs", (_, { settings }) => {
const repos = settings.repos || [];
/**
* Set up the styles and auth provider
*/
export const ExtensionRoot = ({ children }) => {
return (
<>
<Styles />
<AuthProvider serviceName="github" serviceParameters={{ scope: "repo" }}>
<Page repos={repos} />
{children}
</AuthProvider>
</>
);
});
};
70 changes: 70 additions & 0 deletions src/components/PrPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import React from "react";
import { PrTable } from "../components/page/PrTable";
import { searchForPr } from "../lib/github";
import GithubQuery from "../lib/query";
import { useGithubApi } from "../lib/useGithubApi";

export const PrPanel: React.FC<{
filter: string;
repos: string[];
}> = ({ filter, repos }) => {
const query = [
new GithubQuery()
.repo(...repos, { quote: true })
.is("pr")
.toQuery(),
filter,
]
.filter(Boolean)
.join(" ");

const { authed, error, loading, data, fetchData } = useGithubApi(
async (api) => {
const { edges } = await searchForPr(api, {
query,
includeStatus: true,
includeReviews: true,
count: 10,
});
return edges.map((e) => e.node);
},
{},
[query]
);

if (!authed || error) {
return (
<div className="page-content empty-state">
<div
className="empty-state__content"
style={{ textAlign: "center", paddingTop: 50 }}
>
<p>Authenticate with GitHub to get started.</p>
<aha-button type="primary" onClick={() => fetchData()}>
Authenticate with GitHub
</aha-button>
</div>
</div>
);
}

if (loading || !data) {
return (
<div className="page-content empty-state">
<div className="empty-state__content">
<div
style={{
fontSize: 36,
justifyContent: "center",
alignItems: "center",
}}
>
<aha-spinner />
</div>
</div>
</div>
);
}

return <PrTable prs={data} />;
};
4 changes: 1 addition & 3 deletions src/components/Styles.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from "react";
import css from "../lib/css";

const Styles = () => {
export const Styles = () => {
return (
<style>
{css`
Expand Down Expand Up @@ -197,5 +197,3 @@ const Styles = () => {
</style>
);
};

export default Styles;
10 changes: 5 additions & 5 deletions src/components/page/Page.js → src/components/page/PrPage.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { useMemo } from "react";
import BranchTable from "./BranchTable";
import PrTable from "./PrTable";
import GithubQuery from "../../lib/query";
import { useGithubApi } from "../../lib/useGithubApi";
import BranchTable from "./BranchTable";
import { PrTableWithQuery } from "./PrTable";

export const Page = ({ repos }) => {
export const PrPage = ({ repos }) => {
const { authed, error, fetchData } = useGithubApi(async () => {});

const baseQuery = useMemo(
Expand All @@ -22,14 +22,14 @@ export const Page = ({ repos }) => {

<div className="subsection">
<h3>Open</h3>
<PrTable
<PrTableWithQuery
query={baseQuery.is("pr").author("@me").state("open").toQuery()}
/>
</div>

<div className="subsection">
<h3>Closed</h3>
<PrTable
<PrTableWithQuery
query={baseQuery.is("pr").author("@me").state("closed").toQuery()}
/>
</div>
Expand Down
79 changes: 32 additions & 47 deletions src/components/page/PrTable.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import React, { useEffect, useState } from "react";
import { prStatusCommit, searchForPr } from "../../lib/github";
import { loadRelatedFeatures } from "../../lib/loadRelatedFeatures";
import { useGithubApi } from "../../lib/useGithubApi";
import { ExternalLink } from "../ExternalLink";
import { PrReviewStatus } from "../PrReviewStatus";
import { PrState } from "../PrState";
import { Status } from "../Status";

const refNumMatcher = /([A-Z][A-Z0-9]*-(([E]|[0-9]+)-)?[0-9]+)/;
/**
* @typedef QueryTableProps
* @prop {string} query
*/

/**
* @typedef TableProps
* @prop {import('../../lib/github').PrForLink[]} prs
*/

/**
* @typedef RowProps
Expand Down Expand Up @@ -53,7 +62,10 @@ const PrRow = ({ pr, feature }) => {
);
};

const PrTable = ({ query }) => {
/**
* @type {React.FC<QueryTableProps>}
*/
export const PrTableWithQuery = ({ query }) => {
const { authed, error, loading, data } = useGithubApi(
async (api) => {
const { edges } = await searchForPr(api, {
Expand All @@ -67,59 +79,34 @@ const PrTable = ({ query }) => {
{},
[query]
);
const [prRecords, setPrRecords] = useState({});

useEffect(() => {
let mounted = true;
if (!data) return;

const refNums = [];
const prsByRefNum = {};
if (!authed || error) return null;
if (loading || !data) return <aha-spinner></aha-spinner>;

for (let pr of data) {
[pr.headRef?.name.toUpperCase(), pr.title]
.map(String)
.map((s) => refNumMatcher.exec(s))
.forEach((match) => {
if (match) {
refNums.push(match[0]);
prsByRefNum[match[0]] = pr;
}
});
}
return <PrTable prs={data} />;
};

if (refNums.length === 0) {
// No records to find
setPrRecords({});
return;
}
/**
* @type {React.FC<TableProps>}
*/
export const PrTable = ({ prs }) => {
const [prRecords, setPrRecords] = useState({});

aha.models.Feature.select("id", "referenceNum", "name", "path")
.where({
id: refNums,
})
.all()
.then((features) => {
if (!mounted) return;
useEffect(() => {
let mounted = true;
if (!prs) return;

const prRecords = {};
for (let feature of features) {
const pr = prsByRefNum[feature.referenceNum];
if (!pr) continue;
prRecords[pr.number] = feature;
}
setPrRecords(prRecords);
});
loadRelatedFeatures(prs).then((prRecords) => {
if (!mounted) return;
setPrRecords(prRecords);
});

return () => {
mounted = false;
};
}, [data]);

if (!authed || error) return null;
if (loading || !data) return <aha-spinner></aha-spinner>;
}, [prs]);

const rows = data.map((pr, idx) => (
const rows = prs.map((pr, idx) => (
<PrRow key={idx} pr={pr} feature={prRecords[pr.number]} />
));

Expand All @@ -137,5 +124,3 @@ const PrTable = ({ query }) => {
</table>
);
};

export default PrTable;
1 change: 1 addition & 0 deletions src/extension.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const IDENTIFIER = "aha-develop.github";
5 changes: 3 additions & 2 deletions src/lib/fields.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const IDENTIFIER = "aha-develop.github";
import { IDENTIFIER } from "../extension";

const PULL_REQUESTS_FIELD = "pullRequests";
const BRANCHES_FIELD = "branches";

Expand Down Expand Up @@ -199,7 +200,7 @@ async function unlinkBranches(record) {

/**
* @param {string} str
* @returns {Promise<Aha.ReferenceInterface & Aha.HasExtensionFields|null>}
* @returns {Promise<(Aha.HasExtensionFields & Aha.ReferenceInterface)|null>}
*/
async function referenceToRecord(str) {
const ahaReference = extractReference(str);
Expand Down
50 changes: 50 additions & 0 deletions src/lib/loadRelatedFeatures.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const refNumMatcher = /([A-Z][A-Z0-9]*-(([E]|[0-9]+)-)?[0-9]+)/;

/**
* Given a list of PRs, load the corresponding features if they exist
*
* @param {import('./github').PrForLink[]} prs
*/
export async function loadRelatedFeatures(prs) {
const refNums = [];
const prsByRefNum = {};

for (let pr of prs) {
[pr.headRef?.name.toUpperCase(), pr.title]
.map(String)
.map((s) => refNumMatcher.exec(s))
.forEach((match) => {
if (match) {
refNums.push(match[0]);
prsByRefNum[match[0]] = pr;
}
});
}

if (refNums.length === 0) {
// No records to find
return {};
}

const features = await aha.models.Feature.select(
"id",
"referenceNum",
"name",
"path"
)
.where({
id: refNums,
})
.all();

/** @type {{[index:string]: Aha.Feature}} */
const prRecords = {};

for (let feature of features) {
const pr = prsByRefNum[feature.referenceNum];
if (!pr) continue;
prRecords[pr.number] = feature;
}

return prRecords;
}
Loading