authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-10-07 22:48:54-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-10-07 22:48:54-07:00
log70eeb3996d557a2e553f817c8e123bf79bbd378c
tree1d60cacd8b024f2a8f30a0a750f5a8b333a40577
parent85a41391e9625d760f5bb2294834c38f42bbf5b8
signature Signed by PGP key B5690EEEBB952194

fix(mobile): stop refreshing Git status on streamed thread updates (#15893)

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

2 files changed, 19 insertions(+), 4 deletions(-)

apps/mobile/src/state/use-selected-thread-git-actions.test.ts+1
...@@ -26,6 +26,7 @@ vi.mock("react", () => ({...@@ -26,6 +26,7 @@ vi.mock("react", () => ({
26 useCallback: (callback: unknown) => callback,26 useCallback: (callback: unknown) => callback,
27 useMemo: (factory: () => unknown) => factory(),27 useMemo: (factory: () => unknown) => factory(),
28 useEffect: () => {},28 useEffect: () => {},
29 useEffectEvent: (callback: unknown) => callback,
29}));30}));
30vi.mock("./session", () => ({31vi.mock("./session", () => ({
31 useEnvironmentScope: (environmentId: unknown, scope: string) =>32 useEnvironmentScope: (environmentId: unknown, scope: string) =>
apps/mobile/src/state/use-selected-thread-git-actions.ts+18-4
...@@ -1,4 +1,4 @@...@@ -1,4 +1,4 @@
1import { useCallback, useEffect, useMemo } from "react";1import { useCallback, useEffect, useEffectEvent, useMemo } from "react";
22
3import { EnvironmentProject, EnvironmentThreadShell } from "@t3tools/client-runtime/state/shell";3import { EnvironmentProject, EnvironmentThreadShell } from "@t3tools/client-runtime/state/shell";
4import type { AtomCommandResult } from "@t3tools/client-runtime/state/runtime";4import type { AtomCommandResult } from "@t3tools/client-runtime/state/runtime";
...@@ -140,12 +140,26 @@ export function useSelectedThreadGitActions() {...@@ -140,12 +140,26 @@ export function useSelectedThreadGitActions() {
140 [refreshStatus, selectedThread, selectedThreadCwd, selectedThreadProject],140 [refreshStatus, selectedThread, selectedThreadCwd, selectedThreadProject],
141 );141 );
142142
143 // Shell updates replace the selected thread object many times per second while a
144 // turn streams, so key the refresh on primitives. The server publishes status after
145 // each turn finishes; this only seeds status when the selection or cwd changes.
146 const selectedEnvironmentId = selectedThread?.environmentId ?? null;
147 const selectedThreadId = selectedThread?.id ?? null;
148 const hasSelectedThreadProject = selectedThreadProject !== null;
149 const refreshOnSelection = useEffectEvent(() => {
150 void refreshSelectedThreadGitStatus({ quiet: true });
151 });
143 useEffect(() => {152 useEffect(() => {
144 if (!selectedThread || !selectedThreadProject) {153 if (
154 selectedEnvironmentId === null ||
155 selectedThreadId === null ||
156 !hasSelectedThreadProject ||
157 selectedThreadCwd === null
158 ) {
145 return;159 return;
146 }160 }
147 void refreshSelectedThreadGitStatus({ quiet: true });161 refreshOnSelection();
148 }, [refreshSelectedThreadGitStatus, selectedThread, selectedThreadProject]);162 }, [selectedEnvironmentId, selectedThreadId, hasSelectedThreadProject, selectedThreadCwd]);
149163
150 const runSelectedThreadGitMutation = useCallback(164 const runSelectedThreadGitMutation = useCallback(
151 async <T, E>(165 async <T, E>(