fix: Android TV press event support (onPress, onLongPress, onPressIn, onPressOut)#3980
Open
JavanPoirier wants to merge 1 commit into
Open
fix: Android TV press event support (onPress, onLongPress, onPressIn, onPressOut)#3980JavanPoirier wants to merge 1 commit into
JavanPoirier wants to merge 1 commit into
Conversation
JavanPoirier
marked this pull request as ready for review
March 29, 2026 18:17
JavanPoirier
commented
Mar 29, 2026
| { | ||
| esExtensionDefault: platform === 'native' ? '.native.js' : '.mjs', | ||
| esExtensions: platform === 'native' ? ['.js'] : ['.mjs'], | ||
| tryExtensions: platform === 'native' ? ['.native.js', '.js'] : ['.js'], |
Contributor
Author
There was a problem hiding this comment.
Check this pls. Without it, my local build would fail to run on Android TV.
JavanPoirier
force-pushed
the
fix-tv-press-events
branch
from
March 29, 2026 20:53
a97be5c to
0e26a5d
Compare
Contributor
Author
Member
|
Sorry this has been taking so long. v2 is finally out so I can get onto it. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On Android TV with the New Architecture (Fabric), none of
onPress,onLongPress,onPressIn, oronPressOutwere firing on Tamagui components when navigating with the D-pad remote. Replaces PR: #3970, Fixes #3957Root Causes
1. RNGH was intercepting TV remote events
isUsingRNGHwastrueon TV because it only checkedgh.isEnabled. RNGH'sGestureDetectorwraps the view in its own native gesture recogniser which does not translate TV remote select-button events into press callbacks — it only handles touch/pointer events.Fix:
isUsingRNGH = gh.isEnabled && !Platform.isTV. TV builds take theuseMainThreadPressEventspath instead.2. Android TV Fabric:
onPressandonLongPressare not valid propsThe Android TV TVViewConfig Fabric codegen spec only declares a subset of props.
onPressandonLongPressare not in that spec. Passing them causes Fabric to callsetter.apply(node, val)wheresetterisundefined→ crash or silent no-op.The select button fires
onClick(notonPress) and long-hold firesonLongClickon Android TV Fabric.tvOS is unaffected —
onPressandonLongPressare valid props there.Fix in
mainThreadPressEvents.native.ts:onPressInandonPressOutare valid in both Android TV and tvOS Fabric specs.3. Responder handlers crashing Android TV Fabric
useMainThreadPressEventssets responder handlers (onStartShouldSetResponder,onResponderGrant, etc.) that are also not in the Android TV Fabric spec. These were already being deleted ineventHandling.native.tsviaandroidTVFabricIncompatibleHandlers, but a subtle bug meantisPressEnabledcould be truthy wheneventswasnull, causing the cleanup not to run in time.Fix:
4.
tamagui-build: native imports not resolving to.native.jsin local buildsWhy does the released NPM version work but not a local build?
tamagui-buildusesbabel-plugin-fully-specifiedto rewrite bare extension-less imports (e.g.'./helpers/mainThreadPressEvents') to fully-specified paths (e.g.'./helpers/mainThreadPressEvents.native.js') for native builds.The plugin is configured with
tryExtensions— a list of file extensions to check on disk to verify the target exists before rewriting the import. The previous configuration only hadtryExtensions: ['.js']for native builds.The failure scenario:
When
tamagui-buildruns the native build pass, it compiles only.native.tsfiles. At the point babel runs on those compiled outputs,mainThreadPressEvents.js(the web stub) does not yet exist on disk — the web build pass hasn't run yet. The plugin checks for.js, finds nothing, and silently skips rewriting the import.The import stays as bare
'./helpers/mainThreadPressEvents'. Metro then resolves it using its own platform extension rules. In most cases Metro correctly prefers.native.js, but in some environments (custom resolvers, Expo prebuild, or when symlinked from a monorepo workspace) the bare import resolves to the web stub instead — a no-op that exports an empty function.The released NPM version works because the published package is built with web and native passes running sequentially in a controlled CI environment. By the time the native babel pass runs, the web build's
mainThreadPressEvents.jsalready exists on disk, the plugin finds it undertryExtensions: ['.js'], and correctly rewrites to.native.js. Local developer builds usingbun run buildorbun run watchdo not have this ordering guarantee.Fix:
By checking
.native.jsfirst, the plugin findsmainThreadPressEvents.native.jsimmediately (it was just compiled in this same pass) and rewrites the import correctly — regardless of whether the web stub has been built yet.5.
setupMatchMediacalling with invalid argument on TVsetupMatchMediacould be called with a non-function argument (e.g. from third-party integrations). Previously this only logged in dev — in production it would still attempt to assign the invalid value as thematchMediaimplementation, causingwindow.matchMedia is not a functionerrors later.Fix: Added an unconditional
returnearly exit when_is not a function, and moved the dev warning inside that branch. Also assigns the implementation toglobalThis['matchMedia']so third-party code that callswindow.matchMediadirectly (bypassing Tamagui's re-exported helper) gets the correct implementation.Files Changed
code/core/web/src/eventHandling.native.tsisPressEnablednull guard; addPlatformimportcode/core/web/src/helpers/mainThreadPressEvents.native.tsonPress→onClickandonLongPress→onLongClickon Android TV; keeponPress/onLongPresson tvOScode/core/web/src/helpers/matchMedia.native.tsglobalThis.matchMediacode/packages/build/tamagui-build.jstryExtensions: ['.native.js', '.js']for native buildsFixes Issue #3957