authorgravatar for maria@kuuro.netmaria <maria@kuuro.net> 2026-09-21 00:16:10-03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-21 00:16:10-03:00
loga9ab9049bbbab89fe23dcfb26c3169ef70a0597e
tree6ea7f6820d06fe706f881698e33c31ed834945e9
parentdca84efb53b071691a88c6712751fb7de9dcbfdb
signature Signed by PGP key B5690EEEBB952194

feat(web): show the pull request refresh spinning in the detail header (#12833)

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>

1 files changed, 19 insertions(+), 11 deletions(-)

apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx+19-11
...@@ -849,6 +849,9 @@ export function PullRequestDetailPanel({...@@ -849,6 +849,9 @@ export function PullRequestDetailPanel({
849 // and at worst answer from it.849 // and at worst answer from it.
850 const invalidate = useAtomCommand(pullRequestEnvironment.invalidate, { reportFailure: false });850 const invalidate = useAtomCommand(pullRequestEnvironment.invalidate, { reportFailure: false });
851 const [isInvalidating, setIsInvalidating] = useState(false);851 const [isInvalidating, setIsInvalidating] = useState(false);
852 // One word for "the host is being asked again", whichever of the two halves is in flight:
853 // the invalidation round trip, then the detail read it kicks off.
854 const refreshing = isInvalidating || detailQuery.isPending;
852 const refreshFromHost = useCallback(async () => {855 const refreshFromHost = useCallback(async () => {
853 setIsInvalidating(true);856 setIsInvalidating(true);
854 try {857 try {
...@@ -1987,18 +1990,29 @@ export function PullRequestDetailPanel({...@@ -1987,18 +1990,29 @@ export function PullRequestDetailPanel({
1987 <MenuTrigger1990 <MenuTrigger
1988 render={1991 render={
1989 <Button1992 <Button
1990 aria-label="More pull request actions"1993 aria-label={
1994 refreshing ? "Refreshing pull request" : "More pull request actions"
1995 }
1991 className="size-6"1996 className="size-6"
1992 size="icon-xs"1997 size="icon-xs"
1993 variant="ghost-muted"1998 variant="ghost-muted"
1994 />1999 />
1995 }2000 }
1996 >2001 >
1997 <MoreHorizontalIcon className="size-4" />2002 {/* The refresh lives in this menu, so while one runs the trigger wears
2003 the spinning glyph in place of the dots: the reader sees the panel
2004 is fetching without a control appearing or the row shifting. */}
2005 {refreshing ? (
2006 <RefreshIcon refreshing className="size-4" />
2007 ) : (
2008 <MoreHorizontalIcon className="size-4" />
2009 )}
1998 </MenuTrigger>2010 </MenuTrigger>
1999 }2011 }
2000 />2012 />
2001 <TooltipPopup>More pull request actions</TooltipPopup>2013 <TooltipPopup>
2014 {refreshing ? "Refreshing pull request" : "More pull request actions"}
2015 </TooltipPopup>
2002 </Tooltip>2016 </Tooltip>
2003 <MenuPopup align="end" side="bottom" className="min-w-72">2017 <MenuPopup align="end" side="bottom" className="min-w-72">
2004 <PullRequestThreadLinks2018 <PullRequestThreadLinks
...@@ -2012,14 +2026,8 @@ export function PullRequestDetailPanel({...@@ -2012,14 +2026,8 @@ export function PullRequestDetailPanel({
2012 }2026 }
2013 onPickerOpenChange={setThreadPickerOpen}2027 onPickerOpenChange={setThreadPickerOpen}
2014 />2028 />
2015 <MenuItem2029 <MenuItem disabled={refreshing} onClick={() => void refreshFromHost()}>
2016 disabled={isInvalidating || detailQuery.isPending}2030 <RefreshIcon className="size-3.5" refreshing={refreshing} />
2017 onClick={() => void refreshFromHost()}
2018 >
2019 <RefreshIcon
2020 className="size-3.5"
2021 refreshing={isInvalidating || detailQuery.isPending}
2022 />
2023 Refresh2031 Refresh
2024 </MenuItem>2032 </MenuItem>
2025 <MenuItem disabled={handoff !== null} onClick={askAboutPullRequest}>2033 <MenuItem disabled={handoff !== null} onClick={askAboutPullRequest}>