import { SymbolView } from "expo-symbols";
import { useCallback, useEffect, useMemo, useState } from "react";
import {
type ColorValue,
Modal,
Pressable,
ScrollView,
useWindowDimensions,
View,
} from "react-native";
import { Gesture, GestureDetector } from "react-native-gesture-handler";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import Animated, {
runOnJS,
useAnimatedStyle,
useSharedValue,
withTiming,
} from "react-native-reanimated";
import { useThemeColor } from "../../lib/useThemeColor";
import { AppText as Text } from "../../components/AppText";
import { StatusPill } from "../../components/StatusPill";
import { useProjects, useThreadShells } from "../../state/entities";
import { scopedThreadKey } from "../../lib/scopedEntities";
import { relativeTime } from "../../lib/time";
import { resolveThreadStatus } from "./threadPresentation";
import type { EnvironmentThreadShell } from "@t3tools/client-runtime/state/shell";
import { buildThreadNavigationGroups } from "./thread-navigation-groups";
export function ThreadNavigationDrawer(props: {
readonly visible: boolean;
readonly selectedThreadKey: string | null;
readonly onClose: () => void;
readonly onSelectThread: (thread: EnvironmentThreadShell) => void;
readonly onStartNewTask: () => void;
}) {
const insets = useSafeAreaInsets();
const { width } = useWindowDimensions();
const drawerWidth = Math.min(width * 0.84, 360);
const [mounted, setMounted] = useState(props.visible);
const translateX = useSharedValue(-drawerWidth);
const overlayOpacity = useSharedValue(0);
const backdropColor = useThemeColor("--color-backdrop");
const drawerBg = useThemeColor("--color-drawer");
const drawerShadow = useThemeColor("--color-drawer-shadow");
const primaryForeground = useThemeColor("--color-primary-foreground");
const borderSubtleColor = useThemeColor("--color-border-subtle");
useEffect(() => {
if (props.visible) {
setMounted(true);
translateX.value = withTiming(0, { duration: 240 });
overlayOpacity.value = withTiming(1, { duration: 220 });
return;
}
overlayOpacity.value = withTiming(0, { duration: 180 });
translateX.value = withTiming(-drawerWidth, { duration: 220 }, (finished) => {
if (finished) {
runOnJS(setMounted)(false);
}
});
}, [drawerWidth, overlayOpacity, props.visible, translateX]);
const closeDrawer = useCallback(() => {
props.onClose();
}, [props]);
const panGesture = useMemo(
() =>
Gesture.Pan()
.activeOffsetX([-12, 12])
.failOffsetY([-24, 24])
.onUpdate((event) => {
translateX.value = Math.min(0, event.translationX);
})
.onEnd((event) => {
const shouldClose = event.translationX < -drawerWidth * 0.2 || event.velocityX < -500;
if (shouldClose) {
runOnJS(closeDrawer)();
return;
}
translateX.value = withTiming(0, { duration: 180 });
}),
[closeDrawer, drawerWidth, translateX],
);
const drawerStyle = useAnimatedStyle(() => ({
transform: [{ translateX: translateX.value }],
}));
const backdropStyle = useAnimatedStyle(() => ({
opacity: overlayOpacity.value,
}));
if (!mounted) {
return null;
}
return (
Threads
{
props.onClose();
props.onStartNewTask();
}}
className="h-11 w-11 items-center justify-center rounded-full bg-primary"
>
);
}
function ThreadNavigationDrawerContent(props: {
readonly bottomInset: number;
readonly borderSubtleColor: ColorValue;
readonly selectedThreadKey: string | null;
readonly onClose: () => void;
readonly onSelectThread: (thread: EnvironmentThreadShell) => void;
}) {
const projects = useProjects();
const threads = useThreadShells();
const groupedThreads = useMemo(
() => buildThreadNavigationGroups({ projects, threads }),
[projects, threads],
);
return (
{groupedThreads.map((group) => (
{group.title}
{group.threads.length === 0 ? (
No threads yet
) : (
group.threads.map((thread, index) => {
const threadKey = scopedThreadKey(thread.environmentId, thread.id);
const selected = props.selectedThreadKey === threadKey;
const status = resolveThreadStatus(thread);
return (
{
props.onSelectThread(thread);
props.onClose();
}}
style={{
paddingHorizontal: 16,
paddingVertical: 15,
borderTopWidth: index === 0 ? 0 : 1,
borderTopColor: props.borderSubtleColor,
backgroundColor: selected ? undefined : "transparent",
}}
className={selected ? "bg-subtle" : undefined}
>
{thread.title}
{relativeTime(thread.updatedAt ?? thread.createdAt)}
{status ? : null}
);
})
)}
))}
);
}