Skip to content

fix(useHold): resolve taps without depending on key-up#35

Merged
chiefcll merged 2 commits into
mainfrom
fix/usehold-keyup-independent
Jun 19, 2026
Merged

fix(useHold): resolve taps without depending on key-up#35
chiefcll merged 2 commits into
mainfrom
fix/usehold-keyup-independent

Conversation

@chiefcll

Copy link
Copy Markdown
Contributor

Problem

On LG webOS TVs, rail cards on the home page never open, while the featured film and search results work. Featured/search call onSelect directly inside onEnter (fires on key-down), but rail cards route through useHold (to support press-and-hold → watchlist).

With performOnEnterImmediately defaulting to false, the tap action (onEnter) only ran inside releaseHold — i.e. it waited for the OK button's key-up / onEnterRelease event. webOS remotes don't reliably emit a key-up for the OK button on many LG models, so releaseHold never ran and the card never opened. The hold-to-watchlist gesture was unaffected because it fires from the key-down timer.

The root flaw: the primary action was gated behind a key-release event that TV remotes don't guarantee.

Fix

Resolve tap vs. hold from the timer and auto-repeat rather than from key-up, in src/primitives/useHold.ts:

  • key-down starts the hold timer.
  • an auto-repeat key-down (e.repeat, which focusManager already passes to the handler) marks the key as still held → the timer resolves to a holdonHold.
  • key-up before the timertaponEnter fires immediately (no latency on platforms that deliver key-up).
  • neither key-up nor auto-repeat → the timer resolves to a taponEnter after holdThreshold ms. This is the key-up-independent path that keeps taps working on webOS.

A fresh key-down also resets stale state, so a hold whose key-up was swallowed doesn't wedge the next press (a pure-timer version would dead-end). performOnEnterImmediately is preserved unchanged for existing consumers.

Tradeoffs

  • Taps incur ~holdThreshold ms latency only when key-up is missing (webOS); where key-up is delivered, taps stay instant.
  • Hold detection relies on the held key emitting auto-repeat. If a webOS held key emits neither key-up nor auto-repeat, it safely resolves to a tap — the primary action fires; the watchlist gesture is simply unavailable.

Tests

Adds tests/useHold.spec.ts (6 cases): tap-with-keyup, webOS tap (no keyup), hold-with-repeat + onRelease, no-double-fire, recovery after a keyup-less hold, and performOnEnterImmediately. All pass. No new typecheck errors.

Reviewer notes

This fixes the library for every webOS consumer of useHold. Worth confirming on an LG unit by logging startHold vs releaseHold on OK — if startHold fires but releaseHold doesn't, that's the definitive signature of this bug.

🤖 Generated with Claude Code

chiefcll and others added 2 commits June 19, 2026 14:34
On TV platforms that don't reliably emit a key-up for the OK button
(notably LG webOS), the only tap path lived in releaseHold gated on the
key-up / onEnterRelease event, so rail cards routed through useHold never
opened. The hold-to-watchlist gesture was unaffected because it fires
from the key-down timer.

Resolve tap vs hold from the timer and auto-repeat instead of key-up:
- key-down starts the hold timer
- an auto-repeat key-down (e.repeat) marks the key held -> timer fires
  onHold
- key-up before the timer fires onEnter immediately (no latency where
  key-up is delivered)
- if neither key-up nor auto-repeat arrives, the timer resolves to a tap
  -> onEnter, keeping taps working on webOS

A fresh key-down also resets stale state so a hold whose key-up was
swallowed doesn't wedge the next press. performOnEnterImmediately is
preserved. Adds tests covering tap, webOS tap, hold, no-double-fire,
recovery, and performOnEnterImmediately.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@chiefcll chiefcll merged commit 359e0b0 into main Jun 19, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant