Skip to content

Commit

Permalink
feat: implement lazy requests
Browse files Browse the repository at this point in the history
  • Loading branch information
AKharytonchyk committed Mar 28, 2024
1 parent 2dcaf85 commit 2e4eb5e
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 8 deletions.
6 changes: 6 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"cSpell.words": [
"mergable",
"mergeable"
]
}
25 changes: 25 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"@mui/icons-material": "^5.15.14",
"@mui/material": "^5.15.14",
"@octokit/rest": "^21.0.0-beta.1",
"@tanstack/react-query": "^5.28.9",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
Expand Down
9 changes: 6 additions & 3 deletions src/components/PullRequestCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { PullRequest } from "../models/PullRequest";
import { DesignServices, FileOpen, GitHub, Lock, Visibility } from "@mui/icons-material";
import { PullRequestChecks } from "./PullRequestChecks";
import { PullRequestsApprovals } from "./PullRequestsApprovals";
import { PullRequestMergeCheck } from "./PullRequestMergeCheck";

interface PullRequestCardProps {
pr: PullRequest;
Expand Down Expand Up @@ -41,7 +42,7 @@ const PullRequestCard: React.FC<PullRequestCardProps> = ({ pr }) => {
}

return (
<Card>
<Card sx={{ display: "flex", flexDirection: "column", height: "100%" }}>
<CardActions
sx={{
display: "flex",
Expand All @@ -57,7 +58,7 @@ const PullRequestCard: React.FC<PullRequestCardProps> = ({ pr }) => {
size="small"
sx={{ marginRight: "auto" }}
/>
{pr.locked && <Lock /> }
{pr.locked && <Lock /> }
{pr.draft && <Tooltip title="Draft PR"><DesignServices color="secondary"/></Tooltip>}
{pr.labels.map((label) => (<Chip key={label.id} label={label.name} size="small" color={getLabelColor(label.name)}/> ))}
<Chip
Expand All @@ -67,7 +68,7 @@ const PullRequestCard: React.FC<PullRequestCardProps> = ({ pr }) => {
sx={{ marginY: 1 }}
/>
</CardActions>
<CardContent sx={{ display: "flex", flexDirection: "column" , paddingBottom: 1}}>
<CardContent sx={{ display: "flex", flexDirection: "column" , paddingBottom: 1, height: "100%" }}>
<Typography sx={{ fontSize: 14 }} color="text.secondary" gutterBottom>
PR by {pr.user.login}
</Typography>
Expand All @@ -89,6 +90,8 @@ const PullRequestCard: React.FC<PullRequestCardProps> = ({ pr }) => {
{"|"}
<PullRequestChecks owner = {pr.base.repo.owner.login} repo = {pr.base.repo.name} prNumber = {pr.number}/>
{"|"}
<PullRequestMergeCheck owner={pr.base.repo.owner.login} repo={pr.base.repo.name} prNumber={pr.number} />
{"|"}
<PullRequestsApprovals owner = {pr.base.repo.owner.login} repo = {pr.base.repo.name} prNumber = {pr.number}/>
<Box gap={2} display={'flex'}>
<Link href={pr.html_url} target="_blank" rel="noopener"><Tooltip title="View/Open PR"><Visibility/></Tooltip></Link>
Expand Down
8 changes: 6 additions & 2 deletions src/components/PullRequestChecks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ConfigContext } from "../App";
import { CheckRun } from "../models/CheckRun";
import { Box, Dialog, Link, Tooltip, Typography } from "@mui/material";
import { CheckCircle, Error, ErrorOutline } from "@mui/icons-material";
import { useOnScreen } from "../hooks/useOnScreen";

export type PullRequestChecksProps = {
owner: string;
Expand All @@ -18,17 +19,19 @@ export const PullRequestChecks: React.FC<PullRequestChecksProps> = ({
const { octokit } = React.useContext(ConfigContext);
const [checks, setChecks] = React.useState<CheckRun[]>();
const [open, setOpen] = React.useState(false);
const elementRef = React.useRef<HTMLDivElement>(null);
const isIntersecting = useOnScreen(elementRef, "100px", true);

React.useEffect(() => {
if (!octokit) return;
if (!octokit || !isIntersecting) return;
octokit
.getPRChecksStatus(owner, repo, prNumber)
.then((response) => setChecks(response.data.check_runs));

return () => {
setChecks(undefined);
};
}, [octokit, owner, repo, prNumber]);
}, [octokit, owner, repo, prNumber, isIntersecting]);

const allChecksPassed = React.useMemo(
() => checks?.every((check) => check.conclusion === "success"),
Expand All @@ -38,6 +41,7 @@ export const PullRequestChecks: React.FC<PullRequestChecksProps> = ({
return (
<>
<Typography
ref={elementRef}
color="text.secondary"
onClick={() => !allChecksPassed && setOpen(true)}
sx={{ display: "flex", gap: 1, alignItems: "center", cursor: allChecksPassed ? "default" : "pointer" }}
Expand Down
62 changes: 62 additions & 0 deletions src/components/PullRequestMergeCheck.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React from "react";
import { useOnScreen } from "../hooks/useOnScreen";
import { ConfigContext } from "../App";
import { Tooltip, Typography } from "@mui/material";
import { Block, CallMerge } from "@mui/icons-material";

export type PullRequestMergeCheckProps = {
owner: string;
repo: string;
prNumber: number;
};

export const PullRequestMergeCheck: React.FC<PullRequestMergeCheckProps> = ({
owner,
repo,
prNumber,
}) => {
const elementRef = React.useRef<HTMLDivElement>(null);
const isIntersecting = useOnScreen(elementRef, "100px", true);
const [canBeMerged, setCanBeMerged] = React.useState<{
mergeable: boolean;
mergeableState: string;
}>({ mergeable: false, mergeableState: "" });
const { octokit } = React.useContext(ConfigContext);

React.useEffect(() => {
if (octokit && isIntersecting) {
octokit?.hasMergeConflict(owner, repo, prNumber).then((response) => {
setCanBeMerged({
mergeable: response.mergeable ?? false,
mergeableState: response.mergeable_state,
});
});
}
}, [isIntersecting, octokit, owner, repo, prNumber]);

return (
<Typography
ref={elementRef}
color="text.secondary"
sx={{ display: "flex", gap: 1, alignItems: "center" }}
>
Mergeable:
<Tooltip
title={
canBeMerged.mergeableState === "clean"
? "Can be merged"
: "Merging blocked"
}
>
{canBeMerged.mergeableState === "" ? (
<></>
) : canBeMerged.mergeable &&
canBeMerged.mergeableState === "clean" ? (
<CallMerge color="success" />
) : (
<Block color="error" />
)}
</Tooltip>
</Typography>
);
};
9 changes: 6 additions & 3 deletions src/components/PullRequestsApprovals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from "react";
import { ConfigContext } from "../App";
import { Approvals } from "../models/Approvals";
import { Avatar, Badge, Box, Tooltip } from '@mui/material';
import { useOnScreen } from "../hooks/useOnScreen";

export type PullRequestsApprovalsProps = {
owner: string;
Expand All @@ -16,17 +17,19 @@ export const PullRequestsApprovals: React.FC<PullRequestsApprovalsProps> = ({
}) => {
const { octokit } = React.useContext(ConfigContext);
const [ approvals, setApprovals] = React.useState<Approvals[]>([]);
const elementRef = React.useRef<HTMLDivElement>(null);
const isIntersecting = useOnScreen(elementRef, "100px", true);

React.useEffect(() => {
if (!octokit) return;
if (!octokit || !isIntersecting) return;
octokit
.getPRApprovals(owner, repo, prNumber)
.then((response) => setApprovals(response as Approvals[]));

return () => {
setApprovals([]);
};
}, [octokit, owner, repo, prNumber]);
}, [octokit, owner, repo, prNumber, isIntersecting]);

const getBadgeProps = (state: string): {badgeContent: string, color: "success" | "error" | "warning" | "info"} => {
switch (state) {
Expand All @@ -53,7 +56,7 @@ export const PullRequestsApprovals: React.FC<PullRequestsApprovalsProps> = ({
)), [allApprovals]);

return <>
<Box color="text.secondary" sx={{display: "flex", gap: 1, alignItems: "center", marginRight: "auto" }}>
<Box ref={elementRef} color="text.secondary" sx={{display: "flex", gap: 1, alignItems: "center", marginRight: "auto" }}>
Approvals: <Box sx={{ display: "flex", alignItems: "center" }}> {approvals.length ? approvalAvatars : "No reviews"} </Box>
</Box>
</>;
Expand Down
32 changes: 32 additions & 0 deletions src/hooks/useOnScreen.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useEffect, useState, useRef, RefObject } from 'react';

export const useOnScreen = (ref: RefObject<Element>, rootMargin = '0px', once: boolean = true) => {
const observer = useRef<IntersectionObserver | null>(null);
const [isIntersecting, setIntersecting] = useState(false);

useEffect(() => {
observer.current = new IntersectionObserver(
([entry]) => {
setIntersecting(entry.isIntersecting);
if (once && entry.isIntersecting) {
observer.current?.unobserve(entry.target);
}
},
{
rootMargin,
}
);
}, [rootMargin, once]);

useEffect(() => {
if (ref.current) {
observer.current?.observe(ref.current);
}

return () => {
observer.current?.disconnect();
};
}, [ref]);

return isIntersecting;
}
7 changes: 7 additions & 0 deletions src/service/gitService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,13 @@ export class GitService {
});
}

async hasMergeConflict(owner: string, repo: string, prNumber: number) {
const mergeConflicts = await this.octokit.pulls.get({owner, repo, pull_number:prNumber});
console.log(`Merge conflicts: ${mergeConflicts.data.mergeable} - ${mergeConflicts.data.mergeable_state}`);

return mergeConflicts.data
}

async getPRApprovals(owner: string, repo: string, prNumber: number) {
const reviews = await this.octokit.pulls.listReviews({
owner,
Expand Down

0 comments on commit 2e4eb5e

Please sign in to comment.