Compare commits

...

1 Commits

Author SHA1 Message Date
Gregory Schier
0cab111965 Refresh Git dropdown data on open and fetch periodically
- Add refreshKey to useGit queries so dropdown data refreshes on open
- Convert fetchAll from mutation to query with 10-minute refetch interval
- Re-run status query after fetchAll completes via dataUpdatedAt key
- Use placeholderData to keep previous data during key changes
- Remove disabled state from Push, Pull, and Commit menu items
2026-02-10 15:33:42 -08:00
2 changed files with 32 additions and 16 deletions

View File

@@ -32,22 +32,30 @@ export interface GitCallbacks {
const onSuccess = () => queryClient.invalidateQueries({ queryKey: ['git'] }); const onSuccess = () => queryClient.invalidateQueries({ queryKey: ['git'] });
export function useGit(dir: string, callbacks: GitCallbacks) { export function useGit(dir: string, callbacks: GitCallbacks, refreshKey?: string) {
const mutations = useMemo(() => gitMutations(dir, callbacks), [dir, callbacks]); const mutations = useMemo(() => gitMutations(dir, callbacks), [dir, callbacks]);
const fetchAll = useQuery<void, string>({
queryKey: ['git', 'fetch_all', dir, refreshKey],
queryFn: () => invoke('cmd_git_fetch_all', { dir }),
refetchInterval: 10 * 60_000,
});
return [ return [
{ {
remotes: useQuery<GitRemote[], string>({ remotes: useQuery<GitRemote[], string>({
queryKey: ['git', 'remotes', dir], queryKey: ['git', 'remotes', dir, refreshKey],
queryFn: () => getRemotes(dir), queryFn: () => getRemotes(dir),
placeholderData: (prev) => prev,
}), }),
log: useQuery<GitCommit[], string>({ log: useQuery<GitCommit[], string>({
queryKey: ['git', 'log', dir], queryKey: ['git', 'log', dir, refreshKey],
queryFn: () => invoke('cmd_git_log', { dir }), queryFn: () => invoke('cmd_git_log', { dir }),
placeholderData: (prev) => prev,
}), }),
status: useQuery<GitStatusSummary, string>({ status: useQuery<GitStatusSummary, string>({
refetchOnMount: true, refetchOnMount: true,
queryKey: ['git', 'status', dir], queryKey: ['git', 'status', dir, refreshKey, fetchAll.dataUpdatedAt],
queryFn: () => invoke('cmd_git_status', { dir }), queryFn: () => invoke('cmd_git_status', { dir }),
placeholderData: (prev) => prev,
}), }),
}, },
mutations, mutations,
@@ -152,10 +160,7 @@ export const gitMutations = (dir: string, callbacks: GitCallbacks) => {
}, },
onSuccess, onSuccess,
}), }),
fetchAll: createFastMutation<void, string, void>({
mutationKey: ['git', 'fetch_all', dir],
mutationFn: () => invoke('cmd_git_fetch_all', { dir }),
}),
push: createFastMutation<PushResult, string, void>({ push: createFastMutation<PushResult, string, void>({
mutationKey: ['git', 'push', dir], mutationKey: ['git', 'push', dir],
mutationFn: push, mutationFn: push,

View File

@@ -7,6 +7,7 @@ import { forwardRef } from 'react';
import { openWorkspaceSettings } from '../../commands/openWorkspaceSettings'; import { openWorkspaceSettings } from '../../commands/openWorkspaceSettings';
import { activeWorkspaceAtom, activeWorkspaceMetaAtom } from '../../hooks/useActiveWorkspace'; import { activeWorkspaceAtom, activeWorkspaceMetaAtom } from '../../hooks/useActiveWorkspace';
import { useKeyValue } from '../../hooks/useKeyValue'; import { useKeyValue } from '../../hooks/useKeyValue';
import { useRandomKey } from '../../hooks/useRandomKey';
import { sync } from '../../init/sync'; import { sync } from '../../init/sync';
import { showConfirm, showConfirmDelete } from '../../lib/confirm'; import { showConfirm, showConfirmDelete } from '../../lib/confirm';
import { showDialog } from '../../lib/dialog'; import { showDialog } from '../../lib/dialog';
@@ -36,6 +37,7 @@ export function GitDropdown() {
function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) { function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
const workspace = useAtomValue(activeWorkspaceAtom); const workspace = useAtomValue(activeWorkspaceAtom);
const [refreshKey, regenerateKey] = useRandomKey();
const [ const [
{ status, log }, { status, log },
{ {
@@ -43,7 +45,6 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
deleteBranch, deleteBranch,
deleteRemoteBranch, deleteRemoteBranch,
renameBranch, renameBranch,
fetchAll,
mergeBranch, mergeBranch,
push, push,
pull, pull,
@@ -51,7 +52,7 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
resetChanges, resetChanges,
init, init,
}, },
] = useGit(syncDir, gitCallbacks(syncDir)); ] = useGit(syncDir, gitCallbacks(syncDir), refreshKey);
const localBranches = status.data?.localBranches ?? []; const localBranches = status.data?.localBranches ?? [];
const remoteBranches = status.data?.remoteBranches ?? []; const remoteBranches = status.data?.remoteBranches ?? [];
@@ -172,7 +173,7 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
{ type: 'separator' }, { type: 'separator' },
{ {
label: 'Push', label: 'Push',
disabled: !hasRemotes || ahead === 0, hidden: !hasRemotes,
leftSlot: <Icon icon="arrow_up_from_line" />, leftSlot: <Icon icon="arrow_up_from_line" />,
waitForOnSelect: true, waitForOnSelect: true,
async onSelect() { async onSelect() {
@@ -191,7 +192,7 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
}, },
{ {
label: 'Pull', label: 'Pull',
disabled: !hasRemotes || behind === 0, hidden: !hasRemotes,
leftSlot: <Icon icon="arrow_down_to_line" />, leftSlot: <Icon icon="arrow_down_to_line" />,
waitForOnSelect: true, waitForOnSelect: true,
async onSelect() { async onSelect() {
@@ -210,7 +211,7 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
}, },
{ {
label: 'Commit...', label: 'Commit...',
disabled: !hasChanges,
leftSlot: <Icon icon="git_commit_vertical" />, leftSlot: <Icon icon="git_commit_vertical" />,
onSelect() { onSelect() {
showDialog({ showDialog({
@@ -502,15 +503,25 @@ function SyncDropdownWithSyncDir({ syncDir }: { syncDir: string }) {
]; ];
return ( return (
<Dropdown fullWidth items={items} onOpen={fetchAll.mutate}> <Dropdown fullWidth items={items} onOpen={regenerateKey}>
<GitMenuButton> <GitMenuButton>
<InlineCode className="flex items-center gap-1"> <InlineCode className="flex items-center gap-1">
<Icon icon="git_branch" size="xs" className="opacity-50" /> <Icon icon="git_branch" size="xs" className="opacity-50" />
{currentBranch} {currentBranch}
</InlineCode> </InlineCode>
<div className="flex items-center gap-1.5"> <div className="flex items-center gap-1.5">
{ahead > 0 && <span className="text-xs flex items-center gap-0.5"><span className="text-primary"></span>{ahead}</span>} {ahead > 0 && (
{behind > 0 && <span className="text-xs flex items-center gap-0.5"><span className="text-info"></span>{behind}</span>} <span className="text-xs flex items-center gap-0.5">
<span className="text-primary"></span>
{ahead}
</span>
)}
{behind > 0 && (
<span className="text-xs flex items-center gap-0.5">
<span className="text-info"></span>
{behind}
</span>
)}
</div> </div>
</GitMenuButton> </GitMenuButton>
</Dropdown> </Dropdown>