Skip to content
8 changes: 4 additions & 4 deletions src/components/actions/GeminiReviewButton.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Button } from "@chakra-ui/react";
import { useComment } from "@/hooks/useComment";
import { useErrorToast } from "@/hooks/useErrorToast";
import { useGeminiReview } from "@/hooks/useGeminiReview";

interface GeminiReviewButton {
owner: string;
Expand All @@ -10,13 +10,13 @@ interface GeminiReviewButton {
}

export function GeminiReviewButton({ owner, repo, pull_number, state }: GeminiReviewButton) {
const { mutate, isPending, error } = useComment();
const { mutate, isPending, error } = useGeminiReview();
const disabled = isPending || state === "closed";

useErrorToast("gemini-review-error", "Failed to post a comment", error);
useErrorToast("gemini-review-error", "Failed to request Gemini review", error);

return (
<Button onClick={() => mutate({ owner, repo, pull_number, body: "/gemini review" })} disabled={disabled}>
<Button onClick={() => mutate({ owner, repo, pull_number })} disabled={disabled}>
Gemini review
</Button>
);
Expand Down
62 changes: 62 additions & 0 deletions src/hooks/useGeminiReview.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useApiClient } from "./useApiClient";

const GEMINI_USER_LOGIN = "gemini-code-assist[bot]";
const GEMINI_REVIEW_COMMAND = "/gemini review";

type MutationProps = {
owner: string;
repo: string;
pull_number: number;
};

export function useGeminiReview() {
const qc = useQueryClient();
const { octokit } = useApiClient();

return useMutation({
mutationFn: async ({ owner, repo, pull_number }: MutationProps) => {
const comments = await octokit.paginate(octokit.issues.listComments, {
owner,
repo,
issue_number: pull_number,
per_page: 100,
});

const geminiComments = comments.filter((c) => c.user?.login === GEMINI_USER_LOGIN);

const reviewComments = await octokit.paginate(octokit.pulls.listReviewComments, {
owner,
repo,
pull_number,
per_page: 100,
});

const geminiReviewComments = reviewComments.filter((c) => c.user?.login === GEMINI_USER_LOGIN);
Comment on lines +19 to +35

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The two paginate calls to fetch issue comments and review comments are executed sequentially. Since they are independent, you can run them in parallel using Promise.all to improve performance by reducing the total wait time for data fetching.

Suggested change
const comments = await octokit.paginate(octokit.issues.listComments, {
owner,
repo,
issue_number: pull_number,
per_page: 100,
});
const geminiComments = comments.filter((c) => c.user?.login === GEMINI_USER_LOGIN);
const reviewComments = await octokit.paginate(octokit.pulls.listReviewComments, {
owner,
repo,
pull_number,
per_page: 100,
});
const geminiReviewComments = reviewComments.filter((c) => c.user?.login === GEMINI_USER_LOGIN);
const [comments, reviewComments] = await Promise.all([
octokit.paginate(octokit.issues.listComments, {
owner,
repo,
issue_number: pull_number,
per_page: 100,
}),
octokit.paginate(octokit.pulls.listReviewComments, {
owner,
repo,
pull_number,
per_page: 100,
}),
]);
const geminiComments = comments.filter((c) => c.user?.login === GEMINI_USER_LOGIN);
const geminiReviewComments = reviewComments.filter((c) => c.user?.login === GEMINI_USER_LOGIN);


for (const comment of geminiComments) {
try {
await octokit.issues.deleteComment({ owner, repo, comment_id: comment.id });
} catch (e) {
console.error(`Failed to delete issue comment ${comment.id}`, e);
}
}

for (const comment of geminiReviewComments) {
try {
await octokit.pulls.deleteReviewComment({ owner, repo, comment_id: comment.id });
} catch (e) {
console.error(`Failed to delete review comment ${comment.id}`, e);
}
}
Comment on lines +37 to +51

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

The loops for deleting comments run sequentially, which can be slow if there are many comments to delete. You can significantly improve performance by running these deletion requests in parallel using Promise.all. This also makes the code more concise and handles errors for individual deletions gracefully without needing explicit try...catch blocks inside loops.

      const deletionPromises = [
        ...geminiComments.map((comment) =>
          octokit.issues.deleteComment({ owner, repo, comment_id: comment.id }).catch((e) => {
            console.error(`Failed to delete issue comment ${comment.id}`, e);
          }),
        ),
        ...geminiReviewComments.map((comment) =>
          octokit.pulls.deleteReviewComment({ owner, repo, comment_id: comment.id }).catch((e) => {
            console.error(`Failed to delete review comment ${comment.id}`, e);
          }),
        ),
      ];

      await Promise.all(deletionPromises);


return octokit.issues.createComment({
owner,
repo,
issue_number: pull_number,
body: GEMINI_REVIEW_COMMAND,
});
},
onSuccess: () => qc.invalidateQueries({ queryKey: ["open-prs"] }),
});
}
Loading