Is there a way to create a single Observable that can differentiate between a regular click (0-100ms) and a long press (at exactly 1000ms)?
pseudocode
- User clicks and holds
mouseup
between 0 - 100ms -> emit click- No mouseup until 1000ms -> emit long press
- (BONUS): Emit separate event called longPressFinished (click or longPress need to be emitted in any case) after the user eventually performs a
mouseup
sometime after the long press event
- (BONUS): Emit separate event called longPressFinished (click or longPress need to be emitted in any case) after the user eventually performs a
Visual representation
time diagram
reproduction
https://codesandbox.io/s/long-press-p4el0?file=/src/index.ts
I have made some progress using:
interface UIEventResponse {
type: UIEventType,
event: UIEvent
}
type UIEventType = 'click' | 'longPress'
import { fromEvent, merge, Observable, race, timer } from "rxjs";
import { map, mergeMap, switchMap, take, takeUntil } from "rxjs/operators";
const clickTimeout = 100
const longPressTimeout = 1000
const mouseDown$ = fromEvent<MouseEvent>(window, "mousedown");
const mouseUp$ = fromEvent<MouseEvent>(window, "mouseup");
const click1$ = merge(mouseDown$).pipe(
switchMap((event) => {
return race(
timer(longPressTimeout).pipe(mapTo(true)),
mouseUp$.pipe(mapTo(false))
);
})
);
However, if the user keeps the button pressed until just before the longPress event can be emitted, it still triggers a click event.
I want to limit the click event to 0-100ms after the mousedown
. If the user holds for one second, it should immediately trigger a long press. My current code only registers the regular click and ignores the long press afterwards:
const click2$: Observable<UIEventResponse> = mouseDown$.pipe(
switchMap((event) => {
return race<UIEventResponse>(
timer(longPressTimeout).pipe(
mapTo({
type: "longPress",
event
})
),
mouseUp$.pipe(
takeUntil(timer(clickTimeout)),
mapTo({
type: "click",
event
})
)
);
})
);
I suspect this is due to the takeUntil
in the second stream of the race
unsubscribing the entire race
. How can I ensure the mouseup
event does not override the first stream in the race
so that the long press event is still triggered?
Any assistance would be greatly appreciated.