import { useCallback, useMemo, useState } from "react";
import { View, Pressable, Text, Platform, Modal, Alert } from "react-native";
import { usePathname, useRouter } from "expo-router";
import { useSafeAreaInsets } from "react-native-safe-area-context";
import { StyleSheet, useUnistyles } from "react-native-unistyles";
import { AudioLines, Users, Plus, Download } from "lucide-react-native";
import { useRealtime } from "@/contexts/realtime-context";
import { useDaemonConnections } from "@/contexts/daemon-connections-context";
import { useFooterControls, FOOTER_HEIGHT } from "@/contexts/footer-controls-context";
import { RealtimeControls } from "./realtime-controls";
import { CreateAgentModal, ImportAgentModal } from "./create-agent-modal";
import Animated, {
FadeIn,
FadeOut,
useAnimatedStyle,
} from "react-native-reanimated";
import { useReanimatedKeyboardAnimation } from "react-native-keyboard-controller";
export function GlobalFooter() {
const { theme } = useUnistyles();
const insets = useSafeAreaInsets();
const pathname = usePathname();
const router = useRouter();
const { isRealtimeMode, startRealtime } = useRealtime();
const { connectionStates } = useDaemonConnections();
const { controls } = useFooterControls();
const [showCreateModal, setShowCreateModal] = useState(false);
const [showImportModal, setShowImportModal] = useState(false);
const [showRealtimeHostPicker, setShowRealtimeHostPicker] = useState(false);
// Guard Reanimated entry/exit transitions on Android to avoid ViewGroup.dispatchDraw crashes
// tracked in react-native-reanimated#8422.
const shouldDisableEntryExitAnimations = Platform.OS === "android";
const realtimeFadeIn = shouldDisableEntryExitAnimations ? undefined : FadeIn.duration(250);
const realtimeFadeOut = shouldDisableEntryExitAnimations ? undefined : FadeOut.duration(250);
// Determine current screen type
const isAgentScreen = pathname?.startsWith("/agent/");
const hasRegisteredControls = !!controls;
const showAgentControls = isAgentScreen && hasRegisteredControls;
const { height: keyboardHeight } = useReanimatedKeyboardAnimation();
const bottomInset = insets.bottom;
const keyboardAnimatedStyle = useAnimatedStyle(
() => {
"worklet";
const absoluteHeight = Math.abs(keyboardHeight.value);
const shift = Math.max(0, absoluteHeight - bottomInset);
return {
transform: [{ translateY: -shift }],
};
},
[bottomInset],
);
const realtimeEligibleHosts = useMemo(() => {
return Array.from(connectionStates.values()).filter((entry) => entry.status === "online");
}, [connectionStates]);
const handleStartRealtime = useCallback(() => {
if (realtimeEligibleHosts.length === 0) {
Alert.alert("No connected hosts", "Connect a host before starting realtime mode.");
return;
}
if (realtimeEligibleHosts.length === 1) {
void startRealtime(realtimeEligibleHosts[0].daemon.id).catch((error) => {
console.error("[GlobalFooter] Failed to start realtime", error);
Alert.alert("Realtime failed", "Unable to start realtime mode for this host.");
});
return;
}
setShowRealtimeHostPicker(true);
}, [realtimeEligibleHosts, startRealtime]);
const handleSelectRealtimeHost = useCallback(
(daemonId: string) => {
setShowRealtimeHostPicker(false);
void startRealtime(daemonId).catch((error) => {
console.error("[GlobalFooter] Failed to start realtime", error);
Alert.alert("Realtime failed", "Unable to start realtime mode for this host.");
});
},
[startRealtime]
);
const handleDismissHostPicker = useCallback(() => {
setShowRealtimeHostPicker(false);
}, []);
if (showAgentControls) {
return (
{controls}
);
}
if (isAgentScreen) {
return null;
}
// For home and orchestrator screens, show action buttons with realtime stacked on top
const nonAgentFooterHeight = isRealtimeMode
? FOOTER_HEIGHT * 2 + insets.bottom
: FOOTER_HEIGHT + insets.bottom;
const iconSize = 24;
const iconStyle = { width: iconSize, height: iconSize };
return (
<>
{/* Realtime controls - only visible when active */}
{isRealtimeMode && (
)}
{/* Action menu */}
router.push("/")}
style={({ pressed }) => [
styles.footerButton,
pressed && styles.buttonPressed,
]}
>
Agents
setShowImportModal(true)}
style={({ pressed }) => [
styles.footerButton,
pressed && styles.buttonPressed,
]}
>
Import
{
console.log("[GlobalFooter] New Agent button pressed");
setShowCreateModal(true);
}}
style={({ pressed }) => [
styles.footerButton,
pressed && styles.buttonPressed,
]}
>
New Agent
[
styles.footerButton,
realtimeEligibleHosts.length === 0 && styles.buttonDisabled,
pressed && realtimeEligibleHosts.length === 0 && styles.buttonPressed,
]}
>
Realtime
setShowCreateModal(false)}
/>
setShowImportModal(false)}
/>
Choose a host
{realtimeEligibleHosts.map((entry) => (
handleSelectRealtimeHost(entry.daemon.id)}
>
{entry.daemon.label}
))}
Cancel
>
);
}
const styles = StyleSheet.create((theme) => ({
container: {
backgroundColor: theme.colors.background,
borderTopWidth: theme.borderWidth[1],
borderTopColor: theme.colors.border,
},
nonAgentContent: {
flexDirection: "column",
},
realtimeSection: {
height: FOOTER_HEIGHT,
borderBottomWidth: theme.borderWidth[1],
borderBottomColor: theme.colors.border,
},
actionButtonContainer: {
flexDirection: "row",
padding: theme.spacing[4],
gap: theme.spacing[3],
height: FOOTER_HEIGHT,
},
footerButton: {
flex: 1,
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
paddingVertical: theme.spacing[3],
gap: theme.spacing[1],
},
footerIconWrapper: {
width: 28,
height: 28,
alignItems: "center",
justifyContent: "center",
},
footerButtonText: {
color: theme.colors.foreground,
fontSize: theme.fontSize.xs,
fontWeight: theme.fontWeight.normal,
},
buttonDisabled: {
opacity: 0.5,
},
buttonPressed: {
opacity: 0.5,
},
hostPickerOverlay: {
flex: 1,
backgroundColor: "rgba(0,0,0,0.5)",
justifyContent: "flex-end",
},
hostPickerBackdrop: {
flex: 1,
},
hostPickerContainer: {
backgroundColor: theme.colors.card,
padding: theme.spacing[4],
borderTopLeftRadius: theme.borderRadius.xl,
borderTopRightRadius: theme.borderRadius.xl,
gap: theme.spacing[3],
},
hostPickerTitle: {
fontSize: theme.fontSize.lg,
fontWeight: theme.fontWeight.semibold,
color: theme.colors.foreground,
textAlign: "center",
},
hostPickerButton: {
paddingVertical: theme.spacing[3],
borderRadius: theme.borderRadius.lg,
backgroundColor: theme.colors.muted,
},
hostPickerButtonText: {
color: theme.colors.foreground,
fontSize: theme.fontSize.sm,
textAlign: "center",
},
hostPickerCancel: {
paddingVertical: theme.spacing[3],
},
hostPickerCancelText: {
color: theme.colors.mutedForeground,
fontSize: theme.fontSize.sm,
textAlign: "center",
},
}));