Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions apps/mobile/src/features/threads/ThreadGitControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ function compactMenuBranchLabel(branch: string): string {
return truncateMiddle(branch, 24);
}

/**
* Short git status for the thread header. Mentions a stopped upstream refresh
* so the overview, which can pull, is reachable from that label.
*/
function compactMenuStatus(gitStatus: VcsStatusResult | null): string {
if (!gitStatus) {
return "Checking status";
Expand All @@ -62,6 +66,9 @@ function compactMenuStatus(gitStatus: VcsStatusResult | null): string {
if (gitStatus.pr?.state === "open") {
parts.push(`PR #${gitStatus.pr.number}`);
}
if (gitStatus.upstreamNeedsAttention) {
parts.push("upstream needs attention");
}

return parts.join(" · ");
}
Expand Down
19 changes: 19 additions & 0 deletions apps/mobile/src/features/threads/git/GitOverviewSheet.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import {
type GitActionRequestInput,
buildMenuItems,
getGitActionDisabledReason,
offersUpstreamAttentionPull,
requiresDefaultBranchConfirmation,
upstreamStatusAttentionMessage,
} from "@t3tools/client-runtime/state/vcs";
import {
resolveThreadPullRequestChains,
Expand Down Expand Up @@ -54,6 +56,10 @@ type GitOverviewSheetProps = StaticScreenProps<{
readonly presentation?: "sheet" | "inspector";
};

/**
* Git overview for a thread. Offers Pull when the branch is behind, and also
* when background upstream refresh has stopped on a cache that is not behind.
*/
export function GitOverviewSheet(props: GitOverviewSheetProps) {
const { layout } = useAdaptiveWorkspaceLayout();
const navigation = useNavigation();
Expand Down Expand Up @@ -217,6 +223,8 @@ export function GitOverviewSheet(props: GitOverviewSheetProps) {
);

const behindCount = gitStatus.data?.behindCount ?? 0;
const upstreamAttention = upstreamStatusAttentionMessage(gitStatus.data);
const showUpstreamAttentionPull = offersUpstreamAttentionPull(gitStatus.data);

// Deterministic pull-to-refresh state. Tying RefreshControl to the query's
// isPending flag left the spinner stuck (the status query reports pending
Expand Down Expand Up @@ -277,6 +285,17 @@ export function GitOverviewSheet(props: GitOverviewSheetProps) {
onPress={() => void gitActions.onPullSelectedThreadBranch()}
/>
</>
) : showUpstreamAttentionPull ? (
<>
{Platform.OS !== "android" ? <View className="ml-12 h-px bg-border" /> : null}
<SheetListRow
icon="arrow.down.circle"
title="Pull latest"
subtitle={upstreamAttention ?? "Upstream status needs attention. Pull to refresh it."}
disabled={busy || !isRepo}
onPress={() => void gitActions.onPullSelectedThreadBranch()}
/>
</>
) : null}
{Platform.OS !== "android" ? <View className="ml-12 h-px bg-border" /> : null}
<SheetListRow
Expand Down
8 changes: 8 additions & 0 deletions apps/mobile/src/features/threads/git/gitSheetComponents.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -133,13 +133,18 @@ export function menuItemIconName(
return "arrow.up.right.circle";
}

/**
* One-line git summary for the overview sheet. Includes a stopped upstream
* refresh so the sheet is not labeled clean while polling has stopped.
*/
export function statusSummary(
gitStatus: {
readonly isRepo?: boolean;
readonly hasWorkingTreeChanges?: boolean;
readonly workingTree?: { readonly files: readonly { readonly path: string }[] };
readonly aheadCount?: number;
readonly behindCount?: number;
readonly upstreamNeedsAttention?: boolean;
readonly pr?: { readonly state?: string; readonly number?: number } | null;
} | null,
): string {
Expand Down Expand Up @@ -167,6 +172,9 @@ export function statusSummary(
if (gitStatus.pr?.state === "open") {
parts.push(`PR #${gitStatus.pr.number} open`);
}
if (gitStatus.upstreamNeedsAttention) {
parts.push("upstream needs attention");
}

return parts.join(" \u00b7 ");
}
5 changes: 5 additions & 0 deletions apps/server/src/git/GitManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,10 @@ export const make = Effect.gen(function* () {
),
);
});
/**
* Reads ahead/behind for the UI. Omits `upstreamNeedsAttention` while background
* refresh is healthy so status payloads stay small.
*/
const readRemoteStatus = Effect.fn("readRemoteStatus")(function* (
cwd: string,
options?: GitRemoteStatusOptions,
Expand Down Expand Up @@ -1303,6 +1307,7 @@ export const make = Effect.gen(function* () {
aheadCount: details.aheadCount,
behindCount: details.behindCount,
aheadOfDefaultCount: details.aheadOfDefaultCount,
...(details.upstreamNeedsAttention ? { upstreamNeedsAttention: true as const } : {}),
pr,
} satisfies VcsStatusRemoteResult;
});
Expand Down
5 changes: 5 additions & 0 deletions apps/server/src/vcs/GitVcsDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ export interface GitRemoteStatusDetails {
aheadCount: number;
behindCount: number;
aheadOfDefaultCount: number;
/**
* True after repeated background fetches for this upstream remote timed out.
* Polling that remote has stopped until an explicit pull, push, or fetch succeeds.
*/
upstreamNeedsAttention: boolean;
}

export interface GitPreparedCommitContext {
Expand Down
Loading
Loading