mirror of
https://github.com/block/buzz.git
synced 2026-10-30 05:51:46 +00:00
## Summary Follow-up batch on the Projects overview (continues merged #1677): - **Repository access restrictions** — repositories the viewer can't reach are surfaced with a reason instead of failing silently. Channel-ACL denials (which arrive as the same 404 as a missing repo, for anti-enumeration) are re-classified using the repository's channel binding and the viewer's memberships (`useRepositoryAccess.ts`, `projectRepoAvailability.ts`). - **Projects loads in seconds instead of minutes** — enumeration no longer crawls every kind:5 deletion event on the relay. It fetches project/repo announcements first, then queries deletions scoped to those coordinates via chunked `#a` filters (3 queries instead of hundreds on staging). - **Activity feed layout polish** — bare event-type glyph beside the headline (no badge circle), timeline spine runs through the avatars connecting consecutive cards, linkable actor/project names are bold in theme foreground, rounded hover state, alignment fixes. - **Create button pinned** — the "+" create menu is pinned to the pane's top-right corner (equal 16px insets) and no longer scrolls away with the page header. - **List controls as a table header** — the scope selector (left) and sort + layout toggle (right) render as the first row of the list container on the Projects/Repositories/PRs/Issues tabs; in card view the identical bar stands alone with the cards below (`ProjectsListHeaderBar.tsx`). - **Repository rows show the git location** — subtitle is `github.com/org/repo` for external repos or `owner/repo` (resolved profile name) for Buzz-hosted ones, instead of repeating the project name (`repositoryDisplayPath`). - **Uniform work-item row heights** — issue rows previously ran the author chip in inline flow, letting the 20px avatar grow the line box ~3px taller than PR rows; both lists now share the same flex subtitle. 📸 Screenshots: [feed layout / pinned button](https://github.com/block/buzz/pull/5073#issuecomment-5214114069) · [list header / repo subtitles / row heights](https://github.com/block/buzz/pull/5073#issuecomment-5217480712). Note: two empty `chore: retrigger CI` commits exist on the branch from working around the Aug 6 GitHub Actions incident; happy to drop them with a signoff rebase before undrafting if preferred. Latest `main` is merged in (`a0cc35220`). ## Test plan - [x] Desktop unit tests (4,493 pass after merging main), Biome, tsc - [x] New unit tests for scoped deletion enumeration and repo availability re-classification - [x] New unit tests for `repositoryDisplayPath` (external, Buzz-hosted, unresolvable) - [x] Screenshot verification of feed layout, connector spine, and pinned button (top + scrolled states) — posted to the PR - [x] Screenshot verification of the list header row (list + card), repo subtitles, and matching PR/issue row heights — posted to the PR - [ ] Manual pass against staging (projects list load time, restricted-repo states) --------- Signed-off-by: Thomas Petersen <thomasp@squareup.com>
122 lines
3.3 KiB
TypeScript
122 lines
3.3 KiB
TypeScript
import { CircleDot, FolderGit2, GitPullRequest, Plus } from "lucide-react";
|
|
import * as React from "react";
|
|
|
|
import { Button } from "@/shared/ui/button";
|
|
import {
|
|
POPOVER_SHADOW_STYLE,
|
|
POPOVER_SURFACE_CLASS,
|
|
} from "@/shared/ui/popoverSurface";
|
|
|
|
const MENU_ITEM_CLASS =
|
|
"flex min-h-9 w-full items-center gap-2 rounded-lg py-2 pl-2 pr-4 text-left text-sm outline-hidden transition-colors hover:bg-muted/50 focus:bg-muted/50 focus:text-foreground focus-visible:ring-1 focus-visible:ring-ring [&_svg]:size-4 [&_svg]:shrink-0";
|
|
|
|
export function ProjectsCreateMenu({
|
|
onCreateIssue,
|
|
onCreateProject,
|
|
onCreatePullRequest,
|
|
}: {
|
|
onCreateIssue: () => void;
|
|
onCreateProject: () => void;
|
|
onCreatePullRequest: () => void;
|
|
}) {
|
|
const [open, setOpen] = React.useState(false);
|
|
const containerRef = React.useRef<HTMLElement>(null);
|
|
|
|
React.useEffect(() => {
|
|
if (!open) return;
|
|
function handlePointerDown(event: PointerEvent) {
|
|
if (!containerRef.current?.contains(event.target as Node)) {
|
|
setOpen(false);
|
|
}
|
|
}
|
|
globalThis.document.addEventListener(
|
|
"pointerdown",
|
|
handlePointerDown,
|
|
true,
|
|
);
|
|
return () =>
|
|
globalThis.document.removeEventListener(
|
|
"pointerdown",
|
|
handlePointerDown,
|
|
true,
|
|
);
|
|
}, [open]);
|
|
|
|
function select(action: () => void) {
|
|
setOpen(false);
|
|
action();
|
|
}
|
|
|
|
return (
|
|
<nav
|
|
aria-label="Create project item"
|
|
className="relative shrink-0"
|
|
onBlurCapture={(event) => {
|
|
if (!event.currentTarget.contains(event.relatedTarget)) {
|
|
setOpen(false);
|
|
}
|
|
}}
|
|
onKeyDown={(event) => {
|
|
if (event.key === "Escape") {
|
|
setOpen(false);
|
|
containerRef.current?.querySelector<HTMLElement>("button")?.focus();
|
|
}
|
|
}}
|
|
onMouseEnter={() => setOpen(true)}
|
|
onMouseLeave={() => setOpen(false)}
|
|
ref={containerRef}
|
|
>
|
|
<Button
|
|
aria-expanded={open}
|
|
aria-haspopup="menu"
|
|
aria-label="Create"
|
|
className="h-8 w-8 rounded-full"
|
|
data-testid="projects-create-menu"
|
|
onClick={() => setOpen(true)}
|
|
size="icon"
|
|
type="button"
|
|
variant="default"
|
|
>
|
|
<Plus className="h-4 w-4" />
|
|
</Button>
|
|
{open ? (
|
|
<div className="absolute right-0 top-full z-50 min-w-48 pt-1">
|
|
<div
|
|
className={`rounded-xl p-1 ${POPOVER_SURFACE_CLASS}`}
|
|
role="menu"
|
|
style={POPOVER_SHADOW_STYLE}
|
|
>
|
|
<button
|
|
className={MENU_ITEM_CLASS}
|
|
onClick={() => select(onCreateProject)}
|
|
role="menuitem"
|
|
type="button"
|
|
>
|
|
<FolderGit2 />
|
|
Project
|
|
</button>
|
|
<button
|
|
className={MENU_ITEM_CLASS}
|
|
onClick={() => select(onCreateIssue)}
|
|
role="menuitem"
|
|
type="button"
|
|
>
|
|
<CircleDot />
|
|
Issue
|
|
</button>
|
|
<button
|
|
className={MENU_ITEM_CLASS}
|
|
onClick={() => select(onCreatePullRequest)}
|
|
role="menuitem"
|
|
type="button"
|
|
>
|
|
<GitPullRequest />
|
|
Pull Request
|
|
</button>
|
|
</div>
|
|
</div>
|
|
) : null}
|
|
</nav>
|
|
);
|
|
}
|