2026-05-05 18:30:19 +02:00

134 lines
4.2 KiB
TypeScript

import type { ActionReturnType } from '../../types';
import { ARE_CALLS_SUPPORTED } from '../../../util/browser/windowEnvironment';
import { getCurrentTabId } from '../../../util/establishMultitabRole';
import { omit } from '../../../util/iteratees';
import { notifyAboutCall } from '../../../util/notifications';
import { onTickEnd } from '../../../util/schedulers';
import { addActionHandler, getGlobal } from '../../index';
import { updateChat, updateChatFullInfo } from '../../reducers';
import { removeGroupCall, updateGroupCall, updateGroupCallParticipant } from '../../reducers/calls';
import { updateTabState } from '../../reducers/tabs';
import { selectChat } from '../../selectors';
import { selectGroupCall, selectPhoneCallUser } from '../../selectors/calls';
import { checkNavigatorUserMediaPermissions, initializeSounds } from '../ui/calls';
addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
switch (update['@type']) {
case 'updateGroupCall': {
if (update.call.connectionState === 'discarded') {
if (global.groupCalls.activeGroupCallId) {
if ('leaveGroupCall' in actions) actions.leaveGroupCall({ shouldRemove: true, tabId: getCurrentTabId() });
return undefined;
} else {
return removeGroupCall(global, update.call.id);
}
}
const groupCall = selectGroupCall(global, update.call.id);
const chatId = groupCall?.chatId;
if (chatId) {
global = updateChat(global, chatId, {
isCallNotEmpty: (groupCall.participantsCount > 0 || Boolean(groupCall.participants?.length)),
});
}
return updateGroupCall(
global,
update.call.id,
omit(update.call, ['connectionState']),
undefined,
update.call.participantsCount,
);
}
case 'updateGroupCallChatId': {
const chat = selectChat(global, update.chatId);
if (chat) {
global = updateChatFullInfo(global, update.chatId, {
groupCallId: update.call.id,
});
}
return global;
}
case 'updateGroupCallParticipants': {
const { groupCallId, participants, nextOffset } = update;
const { currentUserId } = global;
// `vibecalls` should disconnect if the participant is us but from another device
global = getGlobal();
participants.forEach((participant) => {
if (participant.id) {
global = updateGroupCallParticipant(
global, groupCallId, participant.id, participant, Boolean(nextOffset) || currentUserId === participant.id,
);
}
});
if (nextOffset) {
global = updateGroupCall(global, groupCallId, {
nextOffset,
});
}
const groupCall = selectGroupCall(global, groupCallId);
const chatId = groupCall?.chatId;
if (chatId) {
global = updateChat(global, chatId, {
isCallNotEmpty: (groupCall.participantsCount > 0 || Boolean(groupCall.participants?.length)),
});
}
return global;
}
case 'updatePhoneCall': {
if (!ARE_CALLS_SUPPORTED) return undefined;
const {
phoneCall,
currentUserId,
} = global;
const { call } = update;
if (phoneCall) {
if (call.state === 'discarded') {
actions.playGroupCallSound({ sound: 'end' });
if ('hangUp' in actions) actions.hangUp({ tabId: getCurrentTabId() });
return {
...global,
...(call.needRating && { ratingPhoneCall: call }),
};
}
return undefined;
}
const isOutgoing = call?.adminId === currentUserId;
if (!isOutgoing && call.state === 'requested') {
onTickEnd(() => {
global = getGlobal();
const user = selectPhoneCallUser(global);
if (!user) return;
notifyAboutCall({
call,
user,
});
});
initializeSounds();
void checkNavigatorUserMediaPermissions(global, actions, call.isVideo, getCurrentTabId());
global = {
...global,
phoneCall: call,
};
return updateTabState(global, {
isCallPanelVisible: false,
}, getCurrentTabId());
}
}
}
return undefined;
});