I want to recreate the contextmenu event from touch events in RxJS because iOS does not support the contextmenu event on touch.
In plain language, this observable should emit when:
- the touchstart event occurred
- for 2000ms (basically long-press)
- followed by touchend
It should not emit when:
- the touchend occurs in less than 2000ms
- the touchstart event is followed by touchmove event
I can't figure out how to skip if the touchend occurs sooner or if the touchstart is followed by touchmove. This is what I've so far:
const touchStart$ = fromEvent<TouchEvent>(this.el.nativeElement, "touchstart");
const touchEnd$ = fromEvent<TouchEvent>(this.el.nativeElement, "touchend");
const touchMove$ = fromEvent<TouchEvent>(this.el.nativeElement, "touchmove");
const contextmenu$ = touchStart$.pipe(
switchMap(event => touchEnd$.pipe(mapTo(event))),
switchMap(event => timer(2000).pipe(mapTo(event), takeUntil(merge(touchEnd$, touchMove$))))
);
contextmenu$.subscribe($event => {
console.log("CONTEXTMENU EVENT HAPPENED");
});
CodePudding user response:
Your solution can be simplified a little bit. The inner mapTo
isn't necessary, if you don't reuse the name event
, you can just use a single mapTo
at the end of your pipe. Also, you may want to use take(1)
instead of takeUntil(touchMove$)
on the inner switchMap
since you want to end the stream after the first emission:
const longPress$ = this.touchStart$.pipe(
switchMap(event => timer(2000).pipe(
takeUntil(merge(touchMove$, touchEnd$)),
switchMap(() => touchEnd$.pipe(take(1))),
mapTo(event)
))
);
I'm assuming you got the behavior you wanted, but I think normally a longpress will fire when duration passes; i.e. we needn't wait for the touchend
event. If that were the case, it gets even simpler:
const longPress$ = this.touchStart$.pipe(
switchMap(event => timer(2000).pipe(
takeUntil(merge(touchMove$, touchEnd$)),
mapTo(event)
))
);
Here are a couple StackBlitzes with colorful logging: Original | Simplified
CodePudding user response:
I came up with a possible solution:
const touchStart$ = fromEvent<TouchEvent>(this.el.nativeElement, "touchstart");
const touchEnd$ = fromEvent<TouchEvent>(this.el.nativeElement, "touchend");
const touchMove$ = fromEvent<TouchEvent>(this.el.nativeElement, "touchmove");
const contextmenu$ = touchStart$.pipe(
switchMap(event =>
timer(2000).pipe(
mapTo(event),
takeUntil(merge(touchMove$, touchEnd$)),
switchMap(event => touchEnd$.pipe(mapTo(event), takeUntil(touchMove$)))
)
)
);
contextmenu$.subscribe($event => {
console.log("CONTEXT MENU EVENT HAPPENED");
});