refactor and leave logic

Signed-off-by: Timo K <toger5@hotmail.de>
This commit is contained in:
Timo K
2025-09-12 10:28:26 +02:00
parent 8b185b9893
commit b0f91d56ef

View File

@@ -117,7 +117,10 @@ import waitingStyles from "./WaitingForJoin.module.css";
import { prefetchSounds } from "../soundUtils"; import { prefetchSounds } from "../soundUtils";
import { useAudioContext } from "../useAudioContext"; import { useAudioContext } from "../useAudioContext";
// TODO: Dont use this!!! use the correct sound // TODO: Dont use this!!! use the correct sound
import { GenericReaction } from "../reactions"; import genericSoundOgg from "../sound/reactions/generic.ogg?url";
import genericSoundMp3 from "../sound/reactions/generic.mp3?url";
import leftCallSoundMp3 from "../sound/left_call.mp3";
import leftCallSoundOgg from "../sound/left_call.ogg";
const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {}); const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {});
@@ -273,18 +276,18 @@ export const InCallView: FC<InCallViewProps> = ({
const muteAllAudio = useBehavior(muteAllAudio$); const muteAllAudio = useBehavior(muteAllAudio$);
// Call pickup state and display names are needed for waiting overlay/sounds // Call pickup state and display names are needed for waiting overlay/sounds
const callPickupState = useBehavior(vm.callPickupState$); const callPickupState = useBehavior(vm.callPickupState$);
const displaynames = useBehavior(vm.memberDisplaynames$);
// Preload a subtle waiting/ringing tone // Preload a waiting and decline sounds
const [waitingSoundCache, setWaitingSoundCache] = useState<ReturnType< const pickupPhaseSoundCache = useInitial(async () => {
typeof prefetchSounds return prefetchSounds({
> | null>(null); waiting: { mp3: genericSoundMp3, ogg: genericSoundOgg },
useEffect(() => { decline: { mp3: leftCallSoundMp3, ogg: leftCallSoundOgg },
if (!waitingSoundCache && callPickupState === "ringing") { // do we want a timeout sound?
setWaitingSoundCache(prefetchSounds({ waiting: GenericReaction.sound! })); });
} });
}, [waitingSoundCache, callPickupState]);
const waitingAudio = useAudioContext({ const pickupPhaseAudio = useAudioContext({
sounds: waitingSoundCache, sounds: pickupPhaseSoundCache,
latencyHint: "interactive", latencyHint: "interactive",
muted: muteAllAudio, muted: muteAllAudio,
}); });
@@ -351,49 +354,64 @@ export const InCallView: FC<InCallViewProps> = ({
const audioOutputSwitcher = useBehavior(vm.audioOutputSwitcher$); const audioOutputSwitcher = useBehavior(vm.audioOutputSwitcher$);
useSubscription(vm.autoLeave$, onLeave); useSubscription(vm.autoLeave$, onLeave);
// When waiting for pickup, loop a quiet waiting sound // When we enter timeout or decline we will leave the call.
useEffect((): void | (() => void) => { useEffect((): void | (() => void) => {
// if (callPickupState !== "ringing") return; if (callPickupState === "timeout") {
onLeave();
}
if (callPickupState === "decline") {
void pickupPhaseAudio
?.playSound("decline")
.then(() => {
onLeave();
})
.catch((e) => {
logger.error("Failed to play decline sound", e);
});
}
});
// When waiting for pickup, loop a waiting sound
useEffect((): void | (() => void) => {
if (callPickupState !== "ringing") return;
// play immediately and then every ~2.5s while in ringing // play immediately and then every ~2.5s while in ringing
void waitingAudio?.playSound("waiting"); void pickupPhaseAudio?.playSound("waiting");
const id = window.setInterval( const id = window.setInterval(
() => void waitingAudio?.playSound("waiting"), () => void pickupPhaseAudio?.playSound("waiting"),
2500, 2500,
); );
return (): void => window.clearInterval(id); return (): void => window.clearInterval(id);
}, [callPickupState, waitingAudio]); }, [callPickupState, pickupPhaseAudio]);
// Waiting UI overlay // Waiting UI overlay
const waitingOverlay: JSX.Element | null = useMemo(() => { const waitingOverlay: JSX.Element | null = useMemo(() => {
// if (callPickupState !== "ringing") return null; // No overlay if not in ringing state
if (callPickupState !== "ringing") return null;
// Fallback to room state (joined or invited members) // Use room state for other participants data (the one that we likely want to reach)
const roomOthers = [ const roomOthers = [
...matrixRoom.getMembersWithMembership("join"), ...matrixRoom.getMembersWithMembership("join"),
...matrixRoom.getMembersWithMembership("invite"), ...matrixRoom.getMembersWithMembership("invite"),
].filter((m) => m.userId !== client.getUserId()); ].filter((m) => m.userId !== client.getUserId());
const roomOtherIds = Array.from(new Set(roomOthers.map((m) => m.userId))); // Yield if there are not other members in the room.
if (roomOthers.length === 0) return null;
const isOneOnOne = roomOtherIds.length === 1; const otherMember = roomOthers.length > 0 ? roomOthers[0] : undefined;
const otherId = isOneOnOne ? roomOtherIds[0] : undefined; const isOneOnOne = roomOthers.length === 1 && otherMember;
const otherMember = isOneOnOne
? (roomOthers.find((m) => m.userId === otherId) ??
matrixRoom.getMember(otherId!))
: null;
const name: string = isOneOnOne
? (displaynames.get(otherId!) ?? otherMember?.name ?? otherId!)
: "Other participants";
const avatarMxc = otherMember?.getMxcAvatarUrl?.() ?? undefined;
const text = isOneOnOne const text = isOneOnOne
? `Waiting for ${name} to join…` ? `Waiting for ${otherMember.name ?? otherMember.userId} to join…`
: "Waiting for other participants…"; : "Waiting for other participants…";
const avatarMxc = isOneOnOne
? (otherMember.getMxcAvatarUrl?.() ?? undefined)
: (matrixRoom.getMxcAvatarUrl() ?? undefined);
return ( return (
<div className={waitingStyles.overlay}> <div className={waitingStyles.overlay}>
<div className={waitingStyles.content}> <div className={waitingStyles.content}>
<div className={waitingStyles.pulse}> <div className={waitingStyles.pulse}>
<Avatar <Avatar
id={otherId ?? "others"} id={isOneOnOne ? otherMember.userId : matrixRoom.roomId}
name={name} name={isOneOnOne ? otherMember.name : matrixRoom.name}
src={avatarMxc} src={avatarMxc}
size={AvatarSize.XL} size={AvatarSize.XL}
/> />
@@ -404,7 +422,7 @@ export const InCallView: FC<InCallViewProps> = ({
</div> </div>
</div> </div>
); );
}, [client, displaynames, matrixRoom]); }, [callPickupState, client, matrixRoom]);
// Ideally we could detect taps by listening for click events and checking // Ideally we could detect taps by listening for click events and checking
// that the pointerType of the event is "touch", but this isn't yet supported // that the pointerType of the event is "touch", but this isn't yet supported