Skip to content

Commit 7db2436

Browse files
committed
fix: unvanish element-call, still janky tho
1 parent c553cab commit 7db2436

4 files changed

Lines changed: 64 additions & 13 deletions

File tree

src/app/components/CallEmbedProvider.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export function CallEmbedProvider({ children }: CallEmbedProviderProps) {
5555
left: 0,
5656
width: '100%',
5757
height: '50%',
58+
zIndex: 11,
5859
}}
5960
ref={callEmbedRef}
6061
/>

src/app/components/SwipeableChatWrapper.tsx

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ export function SwipeableChatWrapper({
2626
const isMobile = useIsMobile();
2727
const x = useMotionValue(0);
2828

29-
// On mobile, MobileRoomOverlay owns the right-swipe gesture so canSwipeRight
30-
// is always false. canSwipeLeft is only active if Members mode is on.
31-
// If neither direction is active, skip binding entirely, an idle useDrag
32-
// with rubberband still captures and rubber-bands touches, stealing clicks.
3329
const canSwipeRight = !isMobile && !!onOpenSidebar;
3430
const canSwipeLeft =
3531
settings.mobileGestures &&

src/app/components/SwipeableMessageWrapper.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,9 +86,7 @@ function ActiveSwipeWrapper({
8686
rubberband: true,
8787
filterTaps: true,
8888
eventOptions: { passive: true },
89-
// Without this, useDrag calls setPointerCapture on pointerdown, stealing
90-
// the pointer from MobileRoomOverlay on rightward swipes, causing it to
91-
// always see mx=0 and snap back instead of navigating.
89+
// Without this, useDrag calls setPointerCapture on pointerdown, stealing the pointer from MobileRoomOverlay on rightward swipes, causing it to always see mx=0 and snap back instead of navigating.
9290
pointer: { capture: false },
9391
}
9492
);

src/app/pages/MobileRoomOverlay.tsx

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ReactNode, useEffect, useRef } from 'react';
1+
import { ReactNode, useCallback, useEffect, useRef } from 'react';
22
import { animate, motion, useMotionValue } from 'motion/react';
33
import { useDrag } from '@use-gesture/react';
44
import { useAtomValue } from 'jotai';
@@ -13,22 +13,79 @@ const SWIPE_DISTANCE = 80;
1313
const SWIPE_VELOCITY = 0.4;
1414
const SNAP_SPRING = { type: 'spring' as const, stiffness: 600, damping: 50, mass: 0.6 };
1515
const TRANSITION_SPRING = { type: 'spring' as const, stiffness: 380, damping: 36 };
16+
const CAPTURE_THRESHOLD = 8;
1617

1718
export function MobileRoomOverlay({ children }: { children: ReactNode }) {
1819
const settings = useAtomValue(settingsAtom);
1920
const goBack = useBackRoute();
2021
const x = useMotionValue(window.innerWidth);
2122
const divRef = useRef<HTMLDivElement>(null);
23+
const embed = document.querySelector<HTMLElement>('[data-call-embed-container]');
2224

2325
useEffect(() => {
2426
log.log('mounted, starting slide-in animation');
2527
animate(x, 0, TRANSITION_SPRING);
2628
}, [x]);
2729

28-
const navigateBack = () => {
30+
// Sync fixed call embed transform with overlay position
31+
useEffect(() => {
32+
const unsub = x.on('change', (val) => {
33+
if (embed) embed.style.transform = `translateX(${val}px)`;
34+
});
35+
return () => {
36+
unsub();
37+
if (embed) embed.style.transform = '';
38+
};
39+
}, [embed, x]);
40+
41+
// Disable pointer events on the call embed during rightward swipes so
42+
// useDrag on the overlay can receive the gesture instead of the iframe
43+
useEffect(() => {
44+
if (!settings.mobileGestures) return undefined;
45+
46+
let startX = 0;
47+
let startY = 0;
48+
let disabled = false;
49+
50+
const onTouchStart = (e: TouchEvent) => {
51+
startX = e.touches[0]?.clientX ?? 0;
52+
startY = e.touches[0]?.clientY ?? 0;
53+
disabled = false;
54+
};
55+
56+
const onTouchMove = (e: TouchEvent) => {
57+
if (disabled) return;
58+
const dx = (e.touches[0]?.clientX ?? 0) - startX;
59+
const dy = (e.touches[0]?.clientY ?? 0) - startY;
60+
// Only disable for clearly rightward, non-vertical gestures
61+
if (dx > CAPTURE_THRESHOLD && Math.abs(dy) < Math.abs(dx) * 1.5) {
62+
if (embed) embed.style.pointerEvents = 'none';
63+
disabled = true;
64+
}
65+
};
66+
67+
const onTouchEnd = () => {
68+
if (disabled) {
69+
if (embed) embed.style.pointerEvents = '';
70+
disabled = false;
71+
}
72+
};
73+
74+
window.addEventListener('touchstart', onTouchStart, { passive: true });
75+
window.addEventListener('touchmove', onTouchMove, { passive: true });
76+
window.addEventListener('touchend', onTouchEnd, { passive: true });
77+
window.addEventListener('touchcancel', onTouchEnd, { passive: true });
78+
79+
return () => {
80+
window.removeEventListener('touchstart', onTouchStart);
81+
window.removeEventListener('touchmove', onTouchMove);
82+
window.removeEventListener('touchend', onTouchEnd);
83+
window.removeEventListener('touchcancel', onTouchEnd);
84+
};
85+
}, [embed, settings.mobileGestures]);
86+
87+
const navigateBack = useCallback(() => {
2988
log.log('navigateBack — disabling pointer events, starting exit animation');
30-
// Disable hit-testing immediately so the nav beneath becomes interactive
31-
// before the animation completes and the component unmounts.
3289
if (divRef.current) divRef.current.style.pointerEvents = 'none';
3390
animate(x, window.innerWidth, {
3491
...TRANSITION_SPRING,
@@ -37,12 +94,11 @@ export function MobileRoomOverlay({ children }: { children: ReactNode }) {
3794
goBack();
3895
},
3996
});
40-
};
97+
}, [x, goBack]);
4198

4299
const bind = useDrag(
43100
({ active, movement: [mx], velocity: [vx], direction: [dx] }) => {
44101
if (!settings.mobileGestures || !mobileOrTablet()) return;
45-
46102
if (active) {
47103
x.set(Math.max(0, mx));
48104
} else {

0 commit comments

Comments
 (0)