Skip to content

Commit

Permalink
Add "visibilitychange" event handler
Browse files Browse the repository at this point in the history
  • Loading branch information
arnautov-anton committed Jun 28, 2023
1 parent 0a80c5e commit 974fa78
Showing 1 changed file with 72 additions and 15 deletions.
87 changes: 72 additions & 15 deletions packages/react-sdk/src/core/hooks/useTrackElementVisibility.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect } from 'react';
import { useEffect, useMemo, useRef } from 'react';
import { ViewportTracker, VisibilityState } from '@stream-io/video-client';
import { useCall } from '@stream-io/video-react-bindings';

Expand All @@ -13,32 +13,89 @@ export const useTrackElementVisibility = <T extends HTMLElement>({
}) => {
const call = useCall();

const isIntersectingReference = useRef<boolean | null>(null);

const viewportTracker = propsViewportTracker ?? call?.viewportTracker;

const participantVisibility = useMemo(
() => ({
update: (isVisible: boolean) => {
call?.state.updateParticipant(sessionId, (p) => {
// skip update if the participant state is already in the expected state
if (
(isVisible &&
p.viewportVisibilityState === VisibilityState.VISIBLE) ||
(!isVisible &&
p.viewportVisibilityState === VisibilityState.INVISIBLE)
)
return p;

return {
...p,
viewportVisibilityState: isVisible
? VisibilityState.VISIBLE
: VisibilityState.INVISIBLE,
};
});
},
reset: () => {
call?.state.updateParticipant(sessionId, (p) => {
// skip update if the participant state is already in the expected state
if (p.viewportVisibilityState === VisibilityState.UNKNOWN) return p;

return {
...p,
viewportVisibilityState: VisibilityState.UNKNOWN,
};
});
},
}),
[call, sessionId],
);

useEffect(() => {
if (!call || !trackedElement) return;

const handleVisibilityChange = () => {
const isDocumentVisible = document.visibilityState === 'visible';
const isPIP = trackedElement.contains(document.pictureInPictureElement);
if (isPIP) return;
participantVisibility.update(
isDocumentVisible && isIntersectingReference.current !== false,
);
};

document.addEventListener('visibilitychange', handleVisibilityChange);

return () => {
document.removeEventListener('visibilitychange', handleVisibilityChange);
participantVisibility.reset();
};
}, [call, participantVisibility, trackedElement]);

useEffect(() => {
if (!trackedElement || !viewportTracker || !call) return;

const unobserve = viewportTracker.observe(trackedElement, (entry) => {
call.state.updateParticipant(sessionId, (p) => ({
...p,
viewportVisibilityState:
// observer triggers when element is "moved" to be a fullscreen element
// keep it VISIBLE if that happens to prevent fullscreen with placeholder
entry.isIntersecting || document.fullscreenElement === trackedElement
? VisibilityState.VISIBLE
: VisibilityState.INVISIBLE,
}));
isIntersectingReference.current = entry.isIntersecting;

participantVisibility.update(
// observer triggers when element is "moved" to be a fullscreen element
// keep it VISIBLE if that happens to prevent fullscreen with placeholder
entry.isIntersecting ||
document.fullscreenElement === trackedElement ||
trackedElement.contains(document.pictureInPictureElement),
);
});

return () => {
unobserve();
// reset visibility state to UNKNOWN upon cleanup
// so that the layouts that are not actively observed
// can still function normally (runtime layout switching)
call.state.updateParticipant(sessionId, (p) => ({
...p,
viewportVisibilityState: VisibilityState.UNKNOWN,
}));
participantVisibility.reset();

isIntersectingReference.current = null;
};
}, [trackedElement, viewportTracker, call, sessionId]);
}, [trackedElement, viewportTracker, participantVisibility, call]);
};

0 comments on commit 974fa78

Please sign in to comment.