import { Stack, useLocalSearchParams, useRouter } from "expo-router";
import { useCallback, useMemo, useState } from "react";
import * as Arr from "effect/Array";
import * as Option from "effect/Option";
import { pipe } from "effect/Function";
import { Pressable, ScrollView, Text as RNText, View, useColorScheme } from "react-native";
import { useThemeColor } from "../../lib/useThemeColor";
import { useGitStatus, gitStatusManager } from "../../state/use-git-status";
import { dismissGitActionResult, useGitActionProgress } from "../../state/use-git-action-state";
import { EmptyState } from "../../components/EmptyState";
import { LoadingScreen } from "../../components/LoadingScreen";
import { buildThreadRoutePath } from "../../lib/routes";
import { scopedThreadKey } from "../../lib/scopedEntities";
import { connectionTone } from "../connection/connectionTone";
import { useRemoteCatalog } from "../../state/use-remote-catalog";
import {
useRemoteConnectionStatus,
useRemoteEnvironmentState,
} from "../../state/use-remote-environment-registry";
import { useSelectedThreadDetail } from "../../state/use-thread-detail";
import { useThreadSelection } from "../../state/use-thread-selection";
import { GitActionProgressOverlay } from "./GitActionProgressOverlay";
import { ThreadDetailScreen } from "./ThreadDetailScreen";
import { ThreadGitControls } from "./ThreadGitControls";
import { ThreadNavigationDrawer } from "./ThreadNavigationDrawer";
import { useSelectedThreadCommands } from "./use-selected-thread-commands";
import { useSelectedThreadGitActions } from "./use-selected-thread-git-actions";
import { useSelectedThreadGitState } from "./use-selected-thread-git-state";
import { useThreadComposerState } from "./use-thread-composer-state";
function firstRouteParam(value: string | string[] | undefined): string | null {
if (Array.isArray(value)) {
return value[0] ?? null;
}
return value ?? null;
}
export function ThreadRouteScreen() {
const { isLoadingSavedConnection, environmentStateById, pendingConnectionError } =
useRemoteEnvironmentState();
const { connectionState, connectionError: aggregateConnectionError } =
useRemoteConnectionStatus();
const { projects, threads } = useRemoteCatalog();
const { selectedThread, selectedThreadProject, selectedEnvironmentConnection } =
useThreadSelection();
const selectedThreadDetail = useSelectedThreadDetail();
const composer = useThreadComposerState();
const gitState = useSelectedThreadGitState();
const gitActions = useSelectedThreadGitActions();
const commands = useSelectedThreadCommands({
activePendingUserInput: composer.activePendingUserInput,
activePendingUserInputAnswers: composer.activePendingUserInputAnswers,
refreshSelectedThreadGitStatus: gitActions.refreshSelectedThreadGitStatus,
});
const refreshSelectedThread = commands.onRefresh;
const router = useRouter();
const params = useLocalSearchParams<{
environmentId?: string | string[];
threadId?: string | string[];
}>();
const [drawerVisible, setDrawerVisible] = useState(false);
const environmentId = firstRouteParam(params.environmentId);
const threadId = firstRouteParam(params.threadId);
const routeEnvironmentRuntime = environmentId
? (environmentStateById[environmentId] ?? null)
: null;
const routeConnectionState = routeEnvironmentRuntime?.connectionState ?? connectionState;
const routeConnectionError =
pendingConnectionError ?? routeEnvironmentRuntime?.connectionError ?? aggregateConnectionError;
/* ─── Native header theming ──────────────────────────────────────── */
const isDark = useColorScheme() === "dark";
const iconColor = String(useThemeColor("--color-icon"));
const foregroundColor = String(useThemeColor("--color-foreground"));
const secondaryFg = isDark ? "#a3a3a3" : "#525252";
/* ─── Git status for native header trigger ───────────────────────── */
const gitStatus = useGitStatus({
environmentId: selectedThread?.environmentId ?? "",
cwd: selectedThread?.worktreePath ?? selectedThreadProject?.workspaceRoot ?? null,
});
/* ─── Git action progress (for overlay banner) ──────────────────── */
const gitActionProgressTarget = useMemo(
() => ({
environmentId: selectedThread?.environmentId ?? null,
cwd: selectedThread?.worktreePath ?? selectedThreadProject?.workspaceRoot ?? null,
}),
[
selectedThread?.environmentId,
selectedThread?.worktreePath,
selectedThreadProject?.workspaceRoot,
],
);
const gitActionProgress = useGitActionProgress(gitActionProgressTarget);
const handleRefreshGitStatus = useCallback(async () => {
if (!selectedThread) return;
await gitStatusManager.refresh({
environmentId: selectedThread.environmentId,
cwd: selectedThread.worktreePath ?? selectedThreadProject?.workspaceRoot ?? null,
});
}, [selectedThread, selectedThreadProject?.workspaceRoot]);
/** Wraps thread refresh + git status refresh for pull-to-refresh */
const handleRefreshAll = useCallback(async () => {
await refreshSelectedThread();
await handleRefreshGitStatus();
}, [handleRefreshGitStatus, refreshSelectedThread]);
const handleOpenDrawer = useCallback(() => {
setDrawerVisible(true);
}, []);
const handleOpenConnectionEditor = useCallback(() => {
void router.push("/connections");
}, [router]);
if (!environmentId || !threadId) {
return ;
}
if (!selectedThread) {
const stillHydrating =
isLoadingSavedConnection ||
routeConnectionState === "connecting" ||
routeConnectionState === "reconnecting";
if (stillHydrating) {
return ;
}
return (
);
}
if (!selectedThreadDetail) {
return ;
}
const selectedThreadKey = scopedThreadKey(selectedThread.environmentId, selectedThread.id);
const serverConfig =
routeEnvironmentRuntime?.serverConfig ??
pipe(
Object.values(environmentStateById),
Arr.map((runtime) => runtime.serverConfig),
Arr.findFirst((value) => value !== null),
Option.getOrNull,
);
const headerSubtitle = [
selectedThreadProject?.title ?? null,
selectedEnvironmentConnection?.environmentLabel ?? null,
]
.filter(Boolean)
.join(" · ");
return (
<>
(
{
// TODO: trigger rename modal
}}
>
{selectedThreadDetail.title}
{headerSubtitle}
),
}}
/>
setDrawerVisible(false)}
onSelectThread={(thread) => {
router.replace(buildThreadRoutePath(thread));
}}
onStartNewTask={() => router.push("/new")}
/>
>
);
}