6.5 KiB
Learned Facts (DEPRECATED)
DEPRECATED: This file has been replaced by individual fact files in
.claude/learned-facts/. The compressed index is now inline inCLAUDE.mdunder "Learned Facts Index". New facts should be added as individual files using the/reflectcommand. This file is kept for reference only and is no longer auto-imported.
This file previously contained facts about the codebase learned from past sessions.
Facts
-
Native bottom tabs + useRouter conflict: When using
@bottom-tabs/react-navigationwith Expo Router, avoid using theuseRouter()hook in components rendered at the provider level (outside the tab navigator). The hook subscribes to navigation state changes and can cause unexpected tab switches. Use the staticrouterimport fromexpo-routerinstead. (2025-01-09) -
IntroSheet rendering location: The
IntroSheetcomponent is rendered insideIntroSheetProviderwhich wraps the entire navigation stack. Any hooks in IntroSheet that interact with navigation state can affect the native bottom tabs. (2025-01-09) -
Intro modal trigger location: The intro modal trigger logic should be in the
Home.tsxcomponent, not in the tabs_layout.tsx. Triggering modals from tab layout can interfere with native bottom tabs navigation. (2025-01-09) -
Tab folder naming: The tab folders use underscore prefix naming like
(_home)instead of just(home)based on the project's file structure conventions. (2025-01-09) -
macOS header buttons fix: Header buttons (
headerRight/headerLeft) don't respond to touches on macOS Catalyst builds when using standard React NativeTouchableOpacity. Fix by usingPressablefromreact-native-gesture-handlerinstead. The library is already installed andGestureHandlerRootViewwraps the app. (2026-01-10) -
Header button locations: Header buttons are defined in multiple places:
app/(auth)/(tabs)/(home)/_layout.tsx(SettingsButton, SessionsButton, back buttons),components/common/HeaderBackButton.tsx(reusable),components/Chromecast.tsx,components/RoundButton.tsx, and dynamically vianavigation.setOptions()incomponents/home/Home.tsxandapp/(auth)/(tabs)/(home)/downloads/index.tsx. (2026-01-10) -
useNetworkAwareQueryClient limitations: The
useNetworkAwareQueryClienthook usesObject.create(queryClient)which breaks QueryClient methods that use JavaScript private fields (likegetQueriesData,setQueriesData,setQueryData). Only use it when you ONLY needinvalidateQueries. For cache manipulation, use standarduseQueryClientfrom@tanstack/react-query. (2026-01-10) -
Mark as played flow: The "mark as played" button uses
PlayedStatuscomponent →useMarkAsPlayedhook →usePlaybackManager.markItemPlayed(). The hook does optimistic updates viasetQueriesDatabefore calling the API. Located incomponents/PlayedStatus.tsxandhooks/useMarkAsPlayed.ts. (2026-01-10) -
Stack screen header configuration: Sub-pages under
(home)need explicitStack.Screenentries inapp/(auth)/(tabs)/(home)/_layout.tsxwithheaderTransparent: Platform.OS === "ios",headerBlurEffect: "none", and a back button. Without this, pages show with wrong header styling. (2026-01-10) -
MPV tvOS player exit freeze: On tvOS,
mpv_terminate_destroycan deadlock if called while blocking the main thread (e.g., viaqueue.sync). The fix is to runmpv_terminate_destroyonDispatchQueue.global()asynchronously, allowing it to access main thread for AVFoundation/GPU cleanup. Sendquitcommand and drain events first. Located inmodules/mpv-player/ios/MPVLayerRenderer.swift. (2026-01-22) -
MPV avfoundation-composite-osd ordering: On tvOS, the
avfoundation-composite-osdoption MUST be set immediately aftervo=avfoundation, before anyhwdecoptions. Skipping or reordering this causes the app to freeze when exiting the player. Set to "no" on tvOS (prevents gray tint), "yes" on iOS (for PiP subtitle support). (2026-01-22) -
Thread-safe state for stop flags: When using flags like
isStoppingthat control loop termination across threads, the setter must be synchronous (stateQueue.sync) not async, otherwise the value may not be visible to other threads in time. (2026-01-22) -
TV modals must use navigation pattern: On TV, never use overlay/absolute-positioned modals (like
TVOptionSelectorat the page level). They don't handle the back button correctly. Always use the navigation-based modal pattern: Jotai atom + hook that callsrouter.push()+ page inapp/(auth)/. Use the existinguseTVOptionModalhook andtv-option-modal.tsxpage for option selection.TVOptionSelectoris only appropriate as a sub-selector within a navigation-based modal page. (2026-01-24) -
TV grid layout pattern: For TV grids, use ScrollView with flexWrap instead of FlatList/FlashList with numColumns. FlatList's numColumns divides width evenly among columns which causes inconsistent item sizing. Use
flexDirection: "row",flexWrap: "wrap",justifyContent: "center", andgapfor spacing. (2026-01-25) -
TV horizontal padding standard: TV pages should use
TV_HORIZONTAL_PADDING = 60to match other TV pages like Home, Search, etc. The oldTV_SCALE_PADDING = 20was too small. (2026-01-25) -
Native SwiftUI view sizing: When creating Expo native modules with SwiftUI views, the view needs explicit dimensions. Use a
widthprop passed from React Native, set an explicit.frame(width:height:)in SwiftUI, and overrideintrinsicContentSizein the ExpoView wrapper to report the correct size to React Native's layout system. Using.aspectRatio(contentMode: .fit)alone causes inconsistent sizing. (2026-01-25) -
Streamystats components location: Streamystats TV components are at
components/home/StreamystatsRecommendations.tv.tsxandcomponents/home/StreamystatsPromotedWatchlists.tv.tsx. The watchlist detail page (which shows items in a grid) is atapp/(auth)/(tabs)/(watchlists)/[watchlistId].tsx. (2026-01-25) -
Platform-specific file suffix (.tv.tsx) does NOT work: The
.tv.tsxfile suffix does NOT work for either pages or components in this project. Metro bundler doesn't resolve platform-specific suffixes. Instead, usePlatform.isTVconditional rendering within a single file. For pages: checkPlatform.isTVat the top and return the TV component early. For components: create separateMyComponent.tsxandTVMyComponent.tsxfiles and usePlatform.isTVto choose which to render. (2026-01-26)