import { Stack, useLocalSearchParams, useRouter } from "expo-router"; import { useRef, useState } from "react"; import { ActivityIndicator } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import type { SearchBarCommands } from "react-native-screens"; import { useFeedbackBody } from "../../shared/context/FeedbackBodyProvider.js"; import { useFeedbackUi } from "../../shared/context/FeedbackProvider.js"; import { FeedbackBoard } from "../shared/ui/primitives.js"; import { RoadmapBoardContent, RoadmapItemContent, } from "../shared/ui/RoadmapScreen.js"; import { parseRoadmapRouteItem, roadmapRouteHref, roadmapRouteParams, resolveRoadmapRouteItem, } from "./routes.js"; import { useRoutedRoadmap } from "./RoutedRoadmapContext.js"; import { supportsRoadmapBottomToolbar, useStackBottomInset, } from "./stackInsets.js"; import { useStackHeaderHeight } from "./useStackHeaderHeight.js"; /** * Routed roadmap board page with native Expo Router stack controls. * * Transparent headers are accounted for through the measured stack header * height. The built-in bottom search toolbar is rendered only on platforms * where its native slot exists, and its height is included in the board's * bottom inset. Explicit layout insets always take precedence. */ export function RoadmapBoardScreen() { const { messages, theme } = useFeedbackUi(); const { routes, colors, androidToolbarIcons, pageSize, boardHeaderTransparent, topInset, bottomInset, } = useRoutedRoadmap(); const router = useRouter(); const headerHeight = useStackHeaderHeight(); const resolvedBottomInset = useStackBottomInset( bottomInset, supportsRoadmapBottomToolbar, ); const [query, setQuery] = useState(""); const searchRef = useRef(null); return ( <> { searchRef.current?.blur(); router.push( roadmapRouteHref(routes.item, roadmapRouteParams(item)), { relativeToDirectory: true }, ); }} /> setQuery( (event as unknown as { nativeEvent: { text: string } }).nativeEvent .text, ) } obscureBackground={false} allowToolbarIntegration hideNavigationBar={false} textColor={theme.colors.text} /> {supportsRoadmapBottomToolbar && ( )} ); } export function RoadmapItemScreen() { const params = useLocalSearchParams<{ roadmapId?: string | string[]; item?: string | string[]; }>(); const { hooks } = useFeedbackBody(); const { messages, theme } = useFeedbackUi(); const { routes, colors, androidToolbarIcons, entryPageSize, onEntryOpen } = useRoutedRoadmap(); const router = useRouter(); const insets = useSafeAreaInsets(); const roadmapId = Array.isArray(params.roadmapId) ? params.roadmapId[0] : params.roadmapId; const routeItem = parseRoadmapRouteItem(params.item); const roadmapItem = hooks.useRoadmapItem(roadmapId); const item = resolveRoadmapRouteItem(roadmapId, roadmapItem, routeItem); if (!roadmapId) { throw new Error( 'RoadmapItemScreen requires a "roadmapId" dynamic route parameter.', ); } return ( <> {item ? ( <> ) : ( {roadmapItem === undefined ? ( ) : ( messages.roadmap.noItems )} )} { if (router.canGoBack()) { router.back(); } else { router.replace(roadmapRouteHref(routes.board)); } }} tintColor={theme.colors.text} > {messages.entry.back} ); }