mirror of
https://github.com/block/buzz.git
synced 2026-11-05 00:41:28 +00:00
Adds GitHub-style review capabilities to the PR detail view, grounded in what the Buzz protocol supports: draft/ready via NIP-34 status kinds (1630/1633, same shape as `buzz pr status`), and reviewer requests + approvals as labeled kind:1 comments (t=review-request / t=approval) since NIP-34 has no dedicated review kinds. A review card in the conversation flow (above the composer) shows reviewers, review state, and the Approve / Ready-for-review / Convert-to-draft actions, gated to the PR author or repo owner per the existing trusted-actor rules. Also includes earlier UI polish from this branch: grayscale activity heatmap, icon-only view toggles, breadcrumb + tab restructure (Code first, branch picker + local/remote toggle in panel headers), viewer git-identity commit avatar matching, and StatPill label cleanup. Splits hooks.ts and ProjectRepositoryPanel.tsx to stay under the file-size guard. Mock bridge now accepts repo-scoped publishes so PR comments, approvals, and status changes work in e2e; adds project-pr-review spec.
132 lines
4.0 KiB
TypeScript
132 lines
4.0 KiB
TypeScript
import { ChevronDown, GitBranch, UploadCloud } from "lucide-react";
|
|
|
|
import { Button } from "@/shared/ui/button";
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuRadioGroup,
|
|
DropdownMenuRadioItem,
|
|
DropdownMenuTrigger,
|
|
} from "@/shared/ui/dropdown-menu";
|
|
|
|
/** Branch picker shared by the readme and files panel headers. */
|
|
export function RepositoryBranchDropdown({
|
|
branch,
|
|
branchOptions,
|
|
compact,
|
|
onBranchChange,
|
|
}: {
|
|
branch: string;
|
|
branchOptions: string[];
|
|
/** Smaller trigger for inline headers. */
|
|
compact?: boolean;
|
|
onBranchChange: (branch: string) => void;
|
|
}) {
|
|
const selectableBranches =
|
|
branchOptions.length > 0 ? branchOptions : [branch];
|
|
if (!branch) {
|
|
return (
|
|
<span className="truncate font-mono text-sm font-semibold text-foreground">
|
|
—
|
|
</span>
|
|
);
|
|
}
|
|
return (
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button
|
|
className={
|
|
compact
|
|
? "h-6 max-w-full gap-1 px-1.5 font-mono text-xs font-medium"
|
|
: "h-6 max-w-full gap-1.5 px-2 font-mono text-sm font-semibold"
|
|
}
|
|
size="sm"
|
|
type="button"
|
|
variant="ghost"
|
|
>
|
|
<GitBranch className="h-3 w-3 shrink-0 text-muted-foreground" />
|
|
<span className="truncate">{branch}</span>
|
|
<ChevronDown className="h-3.5 w-3.5 shrink-0 text-muted-foreground" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="start" className="min-w-56">
|
|
<DropdownMenuRadioGroup onValueChange={onBranchChange} value={branch}>
|
|
{selectableBranches.map((option) => (
|
|
<DropdownMenuRadioItem key={option} value={option}>
|
|
<span className="truncate font-mono">{option}</span>
|
|
</DropdownMenuRadioItem>
|
|
))}
|
|
</DropdownMenuRadioGroup>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
);
|
|
}
|
|
|
|
/** Props for the compact branch + remote/local controls in panel headers. */
|
|
export type RepoSourceHeaderControls = {
|
|
branch: string;
|
|
branchOptions: string[];
|
|
onBranchChange: (branch: string) => void;
|
|
source: "remote" | "local";
|
|
onSourceChange: (source: "remote" | "local") => void;
|
|
localDisabled: boolean;
|
|
localLabel: string;
|
|
remoteLabel: string;
|
|
/** Push of local commits, shown only when the local source can push. */
|
|
canPush?: boolean;
|
|
onPush?: () => void;
|
|
pushDisabled?: boolean;
|
|
pushPending?: boolean;
|
|
pushTitle?: string;
|
|
};
|
|
|
|
/** Small pill toggle between the remote and local repository source. */
|
|
export function RepoSourceToggle({
|
|
controls,
|
|
}: {
|
|
controls: RepoSourceHeaderControls;
|
|
}) {
|
|
const showPush =
|
|
controls.source === "local" && controls.canPush && controls.onPush;
|
|
return (
|
|
<div className="flex shrink-0 items-center gap-1">
|
|
{showPush ? (
|
|
<Button
|
|
aria-label={
|
|
controls.pushPending
|
|
? "Pushing local commits"
|
|
: "Push local commits"
|
|
}
|
|
className="h-6 w-6 shrink-0 rounded-full"
|
|
disabled={controls.pushDisabled}
|
|
onClick={controls.onPush}
|
|
size="icon"
|
|
title={controls.pushTitle ?? "Push local commits"}
|
|
variant="default"
|
|
>
|
|
<UploadCloud className="h-3.5 w-3.5" />
|
|
</Button>
|
|
) : null}
|
|
<div className="flex shrink-0 items-center gap-0.5 rounded-full bg-muted/40 p-0.5">
|
|
<Button
|
|
className="h-6 rounded-full px-2.5 text-xs"
|
|
onClick={() => controls.onSourceChange("remote")}
|
|
size="sm"
|
|
variant={controls.source === "remote" ? "secondary" : "ghost"}
|
|
>
|
|
{controls.remoteLabel}
|
|
</Button>
|
|
<Button
|
|
className="h-6 rounded-full px-2.5 text-xs"
|
|
disabled={controls.localDisabled}
|
|
onClick={() => controls.onSourceChange("local")}
|
|
size="sm"
|
|
variant={controls.source === "local" ? "secondary" : "ghost"}
|
|
>
|
|
{controls.localLabel}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|