(fix): live reactions, message and call invites
All checks were successful
/ build-web (push) Successful in 7m40s
/ build-desktop (linux) (push) Successful in 12m10s
/ build-mobile (push) Successful in 19m43s
/ release (push) Successful in 3m35s

(fix): call ui
This commit is contained in:
Alois 2026-07-31 00:29:48 +02:00
commit f1874042ba
Signed by: alois
SSH key fingerprint: SHA256:GBzT2DXvAuGV9XIV5W3WrzVpjU54FThmxHXdbz95J24
10 changed files with 203 additions and 88 deletions

View file

@ -26,7 +26,10 @@ export default function InvitePopup({
loading={null}
component={(user) => (
<Dialog open={open} onOpenChange={setOpen}>
<DialogContent className="flex flex-col gap-5 items-center justify-center w-65 h-80">
<DialogContent
showCloseButton={false}
className="flex flex-col gap-5 items-center justify-center w-65 h-80"
>
<Avatar className="size-30">
<AvatarImage src={user.Avatar} />
<AvatarFallback className="text-5xl">
@ -36,14 +39,14 @@ export default function InvitePopup({
<p className="text-xl font-medium">{user.Display}</p>
<div className="w-full flex justify-center gap-3">
<Button
className="w-14 h-14"
className="w-13 h-13!"
variant="destructive"
onClick={() => onAccept(false)}
>
<X className="size-5" />
</Button>
<Button
className="w-14 h-14"
className="w-13 h-13!"
variant="subtleDefault"
onClick={() => onAccept(true)}
>

View file

@ -268,7 +268,7 @@ export default function Base({
flush || fill ? "rounded-none" : "rounded-md"
} ${
type === "user" && isSpeaking
? "border-4 border-(--primary-foreground-alt)/75"
? "border-4 border-(--primary-foreground-alt)/75!"
: fill
? "border-0"
: "border"

View file

@ -155,8 +155,8 @@ export function getRoom(): Room {
}
const remoteAudioElements = new Map<string, HTMLMediaElement>();
let outgoingJingle: HTMLAudioElement | null = null;
let outgoingJingleGeneration = 0;
let callJingle: HTMLAudioElement | null = null;
let callJingleGeneration = 0;
const CALL_SECRET_VERSION = 1;
const SCREEN_SHARE_PREVIEW_MAX_WIDTH = 320;
@ -164,32 +164,32 @@ const SCREEN_SHARE_PREVIEW_MAX_HEIGHT = 180;
const SCREEN_SHARE_PREVIEW_QUALITY = 0.7;
const SCREEN_SHARE_PREVIEW_TIMEOUT_MS = 5000;
async function startOutgoingJingle() {
const generation = ++outgoingJingleGeneration;
stopSound(outgoingJingle);
outgoingJingle = null;
async function startCallJingle(shouldPlay: () => boolean) {
const generation = ++callJingleGeneration;
stopSound(callJingle);
callJingle = null;
const jingle = await requireRuntime(useCall.getState().runtime).load(
"settings.call_jingle",
);
if (
generation !== outgoingJingleGeneration ||
useCall.getState().invitedUserId == null
generation !== callJingleGeneration ||
!shouldPlay()
) {
return;
}
outgoingJingle = playSound(
callJingle = playSound(
jingle === "jingle_2" ? "call_jingle_2" : "call_jingle_1",
true,
);
}
function stopOutgoingJingle() {
outgoingJingleGeneration += 1;
stopSound(outgoingJingle);
outgoingJingle = null;
function stopCallJingle() {
callJingleGeneration += 1;
stopSound(callJingle);
callJingle = null;
}
function protocolBytes(bytes: Uint8Array): Uint8Array<ArrayBuffer> {
@ -918,7 +918,7 @@ export async function connect(callId: string) {
// Tear down the active call session and return the store to a closed state.
export async function disconnect() {
stopOutgoingJingle();
stopCallJingle();
disposeSpeakingDetector();
await clearScreenSharePreview();
@ -993,7 +993,7 @@ export async function joinCall(
});
if (sendInvite && !existingCallId) {
void startOutgoingJingle();
void startCallJingle(() => useCall.getState().invitedUserId != null);
}
if (callSecret) {
@ -1231,12 +1231,14 @@ export function useInitializeCall() {
useCall.setState({
incomingCallInvite: { callId, callSecret, senderId },
});
void startCallJingle(() => useCall.getState().incomingCallInvite != null);
},
[],
);
const setInvitePopupOpen = useCallback((open: boolean) => {
if (!open) {
stopCallJingle();
useCall.setState({ incomingCallInvite: null });
}
}, []);
@ -1245,6 +1247,7 @@ export function useInitializeCall() {
(accepted: boolean) => {
const invite = useCall.getState().incomingCallInvite;
stopCallJingle();
useCall.setState({ incomingCallInvite: null });
if (!invite) {
@ -1292,6 +1295,11 @@ export function useInitializeCall() {
return;
}
const currentCall = useCall.getState();
if (currentCall.callId === CallId && currentCall.state !== "closed") {
return;
}
showCallingScreen(
CallId,
normalizeWrappedCallSecret(CallSecret),
@ -1434,7 +1442,7 @@ export function useInitializeCall() {
};
const onDisconnected = () => {
stopOutgoingJingle();
stopCallJingle();
playSound("call_leave");
useCall.setState({ state: "closed" });
syncParticipantState();
@ -1445,7 +1453,7 @@ export function useInitializeCall() {
};
const onParticipantConnected = () => {
stopOutgoingJingle();
stopCallJingle();
playSound("call_join");
syncAllRemoteTrackSubscriptions();
syncParticipantState();

View file

@ -185,7 +185,7 @@ export default function View() {
}
>
<Base
fill={isImmersiveFocusedView}
fill
flush={isImmersiveFocusedView || isFocusedTileFlush}
type={focusedTileType}
participant={focusedParticipant}