make call UI react to incoming and ongoing calls
This commit is contained in:
parent
a1601b039e
commit
8b611a2867
|
@ -1,8 +1,14 @@
|
|||
import React, { useState } from 'react';
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import styled from 'styled-components';
|
||||
import _ from 'underscore';
|
||||
import { getConversationController } from '../../../session/conversations/ConversationController';
|
||||
import { CallManager } from '../../../session/utils';
|
||||
import {
|
||||
getHasIncomingCall,
|
||||
getHasIncomingCallFrom,
|
||||
getHasOngoingCall,
|
||||
getHasOngoingCallWith,
|
||||
} from '../../../state/selectors/conversations';
|
||||
import { SessionButton, SessionButtonColor } from '../SessionButton';
|
||||
import { SessionWrapperModal } from '../SessionWrapperModal';
|
||||
|
||||
|
@ -59,108 +65,74 @@ const CallWindowControls = styled.div`
|
|||
transform: translateY(-100%);
|
||||
`;
|
||||
|
||||
// type WindowPositionType = {
|
||||
// top: string;
|
||||
// left: string;
|
||||
// } | null;
|
||||
|
||||
type CallStateType = 'connecting' | 'ongoing' | 'incoming' | null;
|
||||
|
||||
const fakeCaller = '054774a456f15c7aca42fe8d245983549000311aaebcf58ce246250c41fe227676';
|
||||
|
||||
// TODO:
|
||||
/**
|
||||
* Add mute input, deafen, end call, possibly add person to call
|
||||
* duration - look at how duration calculated for recording.
|
||||
*/
|
||||
export const CallContainer = () => {
|
||||
const conversations = getConversationController().getConversations();
|
||||
const hasIncomingCall = useSelector(getHasIncomingCall);
|
||||
const incomingCallProps = useSelector(getHasIncomingCallFrom);
|
||||
const ongoingCallProps = useSelector(getHasOngoingCallWith);
|
||||
const hasOngoingCall = useSelector(getHasOngoingCall);
|
||||
|
||||
// TODO:
|
||||
/**
|
||||
* Add mute input, deafen, end call, possibly add person to call
|
||||
* duration - look at how duration calculated for recording.
|
||||
*/
|
||||
|
||||
const [connectionState, setConnectionState] = useState<CallStateType>('incoming');
|
||||
// const [callWindowPosition, setCallWindowPosition] = useState<WindowPositionType>(null)
|
||||
|
||||
// picking a conversation at random to test with
|
||||
const foundConvo = conversations.find(convo => convo.id === fakeCaller);
|
||||
|
||||
if (!foundConvo) {
|
||||
throw new Error('fakeconvo not found');
|
||||
}
|
||||
foundConvo.callState = 'incoming';
|
||||
console.warn('foundConvo: ', foundConvo);
|
||||
|
||||
const firstCallingConvo = _.first(conversations.filter(convo => convo.callState !== undefined));
|
||||
const ongoingOrIncomingPubkey = ongoingCallProps?.id || incomingCallProps?.id;
|
||||
|
||||
//#region input handlers
|
||||
const handleAcceptIncomingCall = async () => {
|
||||
console.warn('accept call');
|
||||
|
||||
if (firstCallingConvo) {
|
||||
setConnectionState('connecting');
|
||||
firstCallingConvo.callState = 'connecting';
|
||||
await CallManager.USER_acceptIncomingCallRequest(fakeCaller);
|
||||
// some delay
|
||||
setConnectionState('ongoing');
|
||||
firstCallingConvo.callState = 'ongoing';
|
||||
if (incomingCallProps?.id) {
|
||||
await CallManager.USER_acceptIncomingCallRequest(incomingCallProps.id);
|
||||
}
|
||||
// set conversationState = setting up
|
||||
};
|
||||
|
||||
const handleDeclineIncomingCall = async () => {
|
||||
// set conversation.callState = null or undefined
|
||||
// close the modal
|
||||
if (firstCallingConvo) {
|
||||
firstCallingConvo.callState = undefined;
|
||||
if (incomingCallProps?.id) {
|
||||
await CallManager.USER_rejectIncomingCallRequest(incomingCallProps.id);
|
||||
}
|
||||
console.warn('declined call');
|
||||
await CallManager.USER_rejectIncomingCallRequest(fakeCaller);
|
||||
};
|
||||
|
||||
const handleEndCall = async () => {
|
||||
// call method to end call connection
|
||||
console.warn('ending the call');
|
||||
await CallManager.USER_rejectIncomingCallRequest(fakeCaller);
|
||||
if (ongoingOrIncomingPubkey) {
|
||||
await CallManager.USER_rejectIncomingCallRequest(ongoingOrIncomingPubkey);
|
||||
}
|
||||
};
|
||||
|
||||
const handleMouseDown = () => {
|
||||
// reposition call window
|
||||
};
|
||||
//#endregion
|
||||
|
||||
return (
|
||||
<>
|
||||
{connectionState === 'connecting' ? 'connecting...' : null}
|
||||
{connectionState === 'ongoing' ? (
|
||||
<CallWindow onMouseDown={handleMouseDown}>
|
||||
<CallWindowInner>
|
||||
<CallWindowHeader>
|
||||
{firstCallingConvo ? firstCallingConvo.getName() : 'Group name not found'}
|
||||
</CallWindowHeader>
|
||||
<VideoContainer />
|
||||
<CallWindowControls>
|
||||
<SessionButton text={'end call'} onClick={handleEndCall} />
|
||||
<SessionButton text={'end call'} onClick={handleEndCall} />
|
||||
</CallWindowControls>
|
||||
</CallWindowInner>
|
||||
</CallWindow>
|
||||
) : null}
|
||||
if (!hasOngoingCall && !hasIncomingCall) {
|
||||
return null;
|
||||
}
|
||||
|
||||
{!connectionState ? (
|
||||
<SessionWrapperModal title={'incoming call'}>'none'</SessionWrapperModal>
|
||||
) : null}
|
||||
if (hasOngoingCall && ongoingCallProps) {
|
||||
return (
|
||||
<CallWindow>
|
||||
<CallWindowInner>
|
||||
<CallWindowHeader>{ongoingCallProps.name}</CallWindowHeader>
|
||||
<VideoContainer />
|
||||
<CallWindowControls>
|
||||
<SessionButton text={'end call'} onClick={handleEndCall} />
|
||||
</CallWindowControls>
|
||||
</CallWindowInner>
|
||||
</CallWindow>
|
||||
);
|
||||
}
|
||||
|
||||
{connectionState === 'incoming' ? (
|
||||
<SessionWrapperModal title={'incoming call'}>
|
||||
<div className="session-modal__button-group">
|
||||
<SessionButton text={'decline'} onClick={handleDeclineIncomingCall} />
|
||||
<SessionButton
|
||||
text={'accept'}
|
||||
onClick={handleAcceptIncomingCall}
|
||||
buttonColor={SessionButtonColor.Green}
|
||||
/>
|
||||
</div>
|
||||
</SessionWrapperModal>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
if (hasIncomingCall) {
|
||||
return (
|
||||
<SessionWrapperModal title={'incoming call'}>
|
||||
<div className="session-modal__button-group">
|
||||
<SessionButton text={'decline'} onClick={handleDeclineIncomingCall} />
|
||||
<SessionButton
|
||||
text={'accept'}
|
||||
onClick={handleAcceptIncomingCall}
|
||||
buttonColor={SessionButtonColor.Green}
|
||||
/>
|
||||
</div>
|
||||
</SessionWrapperModal>
|
||||
);
|
||||
}
|
||||
// display spinner while connecting
|
||||
return null;
|
||||
};
|
||||
|
|
|
@ -28,8 +28,7 @@ import {
|
|||
} from '../../../interactions/conversationInteractions';
|
||||
import { SessionButtonColor } from '../SessionButton';
|
||||
import { getTimerOptions } from '../../../state/selectors/timerOptions';
|
||||
import { ToastUtils } from '../../../session/utils';
|
||||
import { getConversationById } from '../../../data/data';
|
||||
import { CallManager, ToastUtils } from '../../../session/utils';
|
||||
|
||||
const maxNumberOfPinnedConversations = 5;
|
||||
|
||||
|
@ -321,38 +320,32 @@ export function getMarkAllReadMenuItem(conversationId: string): JSX.Element | nu
|
|||
|
||||
export function getStartCallMenuItem(conversationId: string): JSX.Element | null {
|
||||
// TODO: possibly conditionally show options?
|
||||
const callOptions = [
|
||||
{
|
||||
name: 'Video call',
|
||||
value: 'video-call',
|
||||
},
|
||||
{
|
||||
name: 'Audio call',
|
||||
value: 'audio-call',
|
||||
},
|
||||
];
|
||||
// const callOptions = [
|
||||
// {
|
||||
// name: 'Video call',
|
||||
// value: 'video-call',
|
||||
// },
|
||||
// // {
|
||||
// // name: 'Audio call',
|
||||
// // value: 'audio-call',
|
||||
// // },
|
||||
// ];
|
||||
|
||||
return (
|
||||
<Submenu label={'Start call'}>
|
||||
{callOptions.map(item => (
|
||||
<Item
|
||||
key={item.value}
|
||||
onClick={async () => {
|
||||
// TODO: either pass param to callRecipient or call different call methods based on item selected.
|
||||
const convo = await getConversationById(conversationId);
|
||||
if (convo) {
|
||||
// window?.libsession?.Utils.CallManager.USER_callRecipient(
|
||||
// '054774a456f15c7aca42fe8d245983549000311aaebcf58ce246250c41fe227676'
|
||||
// );
|
||||
window?.libsession?.Utils.CallManager.USER_callRecipient(convo.id);
|
||||
convo.callState = 'connecting';
|
||||
}
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
</Item>
|
||||
))}
|
||||
</Submenu>
|
||||
<Item
|
||||
onClick={async () => {
|
||||
// TODO: either pass param to callRecipient or call different call methods based on item selected.
|
||||
const convo = getConversationController().get(conversationId);
|
||||
if (convo) {
|
||||
convo.callState = 'connecting';
|
||||
await convo.commit();
|
||||
|
||||
await CallManager.USER_callRecipient(convo.id);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{'video call'}
|
||||
</Item>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -177,6 +177,8 @@ export const fillConvoAttributesWithDefaults = (
|
|||
});
|
||||
};
|
||||
|
||||
export type CallState = 'offering' | 'incoming' | 'connecting' | 'ongoing' | 'none' | undefined;
|
||||
|
||||
export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
||||
public updateLastMessage: () => any;
|
||||
public throttledBumpTyping: any;
|
||||
|
@ -184,7 +186,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
public markRead: (newestUnreadDate: number, providedOptions?: any) => Promise<void>;
|
||||
public initialPromise: any;
|
||||
|
||||
public callState: 'offering' | 'incoming' | 'connecting' | 'ongoing' | 'none' | undefined;
|
||||
public callState: CallState;
|
||||
|
||||
private typingRefreshTimer?: NodeJS.Timeout | null;
|
||||
private typingPauseTimer?: NodeJS.Timeout | null;
|
||||
|
@ -440,6 +442,7 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
const left = !!this.get('left');
|
||||
const expireTimer = this.get('expireTimer');
|
||||
const currentNotificationSetting = this.get('triggerNotificationsFor');
|
||||
const callState = this.callState;
|
||||
|
||||
// to reduce the redux store size, only set fields which cannot be undefined
|
||||
// for instance, a boolean can usually be not set if false, etc
|
||||
|
@ -544,6 +547,10 @@ export class ConversationModel extends Backbone.Model<ConversationAttributes> {
|
|||
text: lastMessageText,
|
||||
};
|
||||
}
|
||||
|
||||
if (callState) {
|
||||
toRet.callState = callState;
|
||||
}
|
||||
return toRet;
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ export async function handleCallMessage(
|
|||
}
|
||||
await removeFromCache(envelope);
|
||||
|
||||
await CallManager.handleOfferCallMessage(sender, callMessage);
|
||||
CallManager.handleOfferCallMessage(sender, callMessage);
|
||||
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -17,8 +17,9 @@ import { storeOnNode } from '../snode_api/SNodeAPI';
|
|||
import { getSwarmFor } from '../snode_api/snodePool';
|
||||
import { firstTrue } from '../utils/Promise';
|
||||
import { MessageSender } from '.';
|
||||
import { getConversationById, getMessageById } from '../../../ts/data/data';
|
||||
import { getMessageById } from '../../../ts/data/data';
|
||||
import { SNodeAPI } from '../snode_api';
|
||||
import { getConversationController } from '../conversations';
|
||||
|
||||
const DEFAULT_CONNECTIONS = 3;
|
||||
|
||||
|
@ -173,7 +174,7 @@ export async function TEST_sendMessageToSnode(
|
|||
throw new window.textsecure.EmptySwarmError(pubKey, 'Ran out of swarm nodes to query');
|
||||
}
|
||||
|
||||
const conversation = await getConversationById(pubKey);
|
||||
const conversation = getConversationController().get(pubKey);
|
||||
const isClosedGroup = conversation?.isClosedGroup();
|
||||
|
||||
// If message also has a sync message, save that hash. Otherwise save the hash from the regular message send i.e. only closed groups in this case.
|
||||
|
|
|
@ -1,26 +1,11 @@
|
|||
import _ from 'lodash';
|
||||
import { SignalService } from '../../protobuf';
|
||||
import { answerCall, callConnected, endCall, incomingCall } from '../../state/ducks/conversations';
|
||||
import { CallMessage } from '../messages/outgoing/controlMessage/CallMessage';
|
||||
import { ed25519Str } from '../onions/onionPath';
|
||||
import { getMessageQueue } from '../sending';
|
||||
import { PubKey } from '../types';
|
||||
|
||||
const incomingCall = ({ sender }: { sender: string }) => {
|
||||
return { type: 'incomingCall', payload: sender };
|
||||
};
|
||||
const endCall = ({ sender }: { sender: string }) => {
|
||||
return { type: 'endCall', payload: sender };
|
||||
};
|
||||
const answerCall = ({ sender, sdps }: { sender: string; sdps: Array<string> }) => {
|
||||
return {
|
||||
type: 'answerCall',
|
||||
payload: {
|
||||
sender,
|
||||
sdps,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* This field stores all the details received by a sender about a call in separate messages.
|
||||
*/
|
||||
|
@ -28,7 +13,7 @@ const callCache = new Map<string, Array<SignalService.CallMessage>>();
|
|||
|
||||
let peerConnection: RTCPeerConnection | null;
|
||||
|
||||
const ENABLE_VIDEO = true;
|
||||
const ENABLE_VIDEO = false;
|
||||
|
||||
const configuration = {
|
||||
configuration: {
|
||||
|
@ -57,13 +42,12 @@ export async function USER_callRecipient(recipient: string) {
|
|||
|
||||
const mediaDevices = await openMediaDevices();
|
||||
mediaDevices.getTracks().map(track => {
|
||||
window.log.info('USER_callRecipient adding track: ', track);
|
||||
peerConnection?.addTrack(track, mediaDevices);
|
||||
});
|
||||
peerConnection.addEventListener('connectionstatechange', _event => {
|
||||
window.log.info('peerConnection?.connectionState:', peerConnection?.connectionState);
|
||||
window.log.info('peerConnection?.connectionState caller :', peerConnection?.connectionState);
|
||||
if (peerConnection?.connectionState === 'connected') {
|
||||
// Peers connected!
|
||||
window.inboxStore?.dispatch(callConnected({ pubkey: recipient }));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -180,27 +164,32 @@ export async function USER_acceptIncomingCallRequest(fromSender: string) {
|
|||
peerConnection = new RTCPeerConnection(configuration);
|
||||
const mediaDevices = await openMediaDevices();
|
||||
mediaDevices.getTracks().map(track => {
|
||||
window.log.info('USER_acceptIncomingCallRequest adding track ', track);
|
||||
// window.log.info('USER_acceptIncomingCallRequest adding track ', track);
|
||||
peerConnection?.addTrack(track, mediaDevices);
|
||||
});
|
||||
peerConnection.addEventListener('icecandidateerror', event => {
|
||||
console.warn('icecandidateerror:', event);
|
||||
});
|
||||
|
||||
peerConnection.addEventListener('negotiationneeded', async event => {
|
||||
peerConnection.addEventListener('negotiationneeded', event => {
|
||||
console.warn('negotiationneeded:', event);
|
||||
});
|
||||
peerConnection.addEventListener('signalingstatechange', event => {
|
||||
console.warn('signalingstatechange:', event);
|
||||
peerConnection.addEventListener('signalingstatechange', _event => {
|
||||
// console.warn('signalingstatechange:', event);
|
||||
});
|
||||
|
||||
peerConnection.addEventListener('ontrack', event => {
|
||||
console.warn('ontrack:', event);
|
||||
});
|
||||
peerConnection.addEventListener('connectionstatechange', _event => {
|
||||
window.log.info('peerConnection?.connectionState:', peerConnection?.connectionState, _event);
|
||||
window.log.info(
|
||||
'peerConnection?.connectionState recipient:',
|
||||
peerConnection?.connectionState,
|
||||
'with: ',
|
||||
fromSender
|
||||
);
|
||||
if (peerConnection?.connectionState === 'connected') {
|
||||
// Peers connected!
|
||||
window.inboxStore?.dispatch(callConnected({ pubkey: fromSender }));
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -237,7 +226,7 @@ export async function USER_acceptIncomingCallRequest(fromSender: string) {
|
|||
);
|
||||
|
||||
if (lastCandidatesFromSender) {
|
||||
console.warn('found sender ice candicate message already sent. Using it');
|
||||
window.log.info('found sender ice candicate message already sent. Using it');
|
||||
for (let index = 0; index < lastCandidatesFromSender.sdps.length; index++) {
|
||||
const sdp = lastCandidatesFromSender.sdps[index];
|
||||
const sdpMLineIndex = lastCandidatesFromSender.sdpMLineIndexes[index];
|
||||
|
@ -250,7 +239,7 @@ export async function USER_acceptIncomingCallRequest(fromSender: string) {
|
|||
|
||||
await getMessageQueue().sendToPubKeyNonDurably(PubKey.cast(fromSender), callAnswerMessage);
|
||||
|
||||
window.inboxStore?.dispatch(answerCall({ sender: fromSender, sdps }));
|
||||
window.inboxStore?.dispatch(answerCall({ pubkey: fromSender }));
|
||||
}
|
||||
|
||||
// tslint:disable-next-line: function-name
|
||||
|
@ -261,7 +250,7 @@ export async function USER_rejectIncomingCallRequest(fromSender: string) {
|
|||
});
|
||||
callCache.delete(fromSender);
|
||||
|
||||
window.inboxStore?.dispatch(endCall({ sender: fromSender }));
|
||||
window.inboxStore?.dispatch(endCall({ pubkey: fromSender }));
|
||||
window.log.info('sending END_CALL MESSAGE');
|
||||
|
||||
await getMessageQueue().sendToPubKeyNonDurably(PubKey.cast(fromSender), endCallMessage);
|
||||
|
@ -271,18 +260,15 @@ export function handleEndCallMessage(sender: string) {
|
|||
callCache.delete(sender);
|
||||
//
|
||||
// FIXME audric trigger UI cleanup
|
||||
window.inboxStore?.dispatch(endCall({ sender }));
|
||||
window.inboxStore?.dispatch(endCall({ pubkey: sender }));
|
||||
}
|
||||
|
||||
export async function handleOfferCallMessage(
|
||||
sender: string,
|
||||
callMessage: SignalService.CallMessage
|
||||
) {
|
||||
export function handleOfferCallMessage(sender: string, callMessage: SignalService.CallMessage) {
|
||||
if (!callCache.has(sender)) {
|
||||
callCache.set(sender, new Array());
|
||||
}
|
||||
callCache.get(sender)?.push(callMessage);
|
||||
window.inboxStore?.dispatch(incomingCall({ sender }));
|
||||
window.inboxStore?.dispatch(incomingCall({ pubkey: sender }));
|
||||
}
|
||||
|
||||
export async function handleCallAnsweredMessage(
|
||||
|
@ -298,7 +284,7 @@ export async function handleCallAnsweredMessage(
|
|||
}
|
||||
|
||||
callCache.get(sender)?.push(callMessage);
|
||||
window.inboxStore?.dispatch(incomingCall({ sender }));
|
||||
window.inboxStore?.dispatch(answerCall({ pubkey: sender }));
|
||||
const remoteDesc = new RTCSessionDescription({ type: 'answer', sdp: callMessage.sdps[0] });
|
||||
if (peerConnection) {
|
||||
await peerConnection.setRemoteDescription(remoteDesc);
|
||||
|
@ -320,7 +306,7 @@ export async function handleIceCandidatesMessage(
|
|||
}
|
||||
|
||||
callCache.get(sender)?.push(callMessage);
|
||||
window.inboxStore?.dispatch(incomingCall({ sender }));
|
||||
// window.inboxStore?.dispatch(incomingCall({ pubkey: sender }));
|
||||
if (peerConnection) {
|
||||
// tslint:disable-next-line: prefer-for-of
|
||||
for (let index = 0; index < callMessage.sdps.length; index++) {
|
||||
|
|
|
@ -3,6 +3,7 @@ import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
|
|||
import { getConversationController } from '../../session/conversations';
|
||||
import { getFirstUnreadMessageIdInConversation, getMessagesByConversation } from '../../data/data';
|
||||
import {
|
||||
CallState,
|
||||
ConversationNotificationSettingType,
|
||||
ConversationTypeEnum,
|
||||
} from '../../models/conversation';
|
||||
|
@ -243,6 +244,7 @@ export interface ReduxConversationType {
|
|||
currentNotificationSetting?: ConversationNotificationSettingType;
|
||||
|
||||
isPinned?: boolean;
|
||||
callState?: CallState;
|
||||
}
|
||||
|
||||
export interface NotificationForConvoOption {
|
||||
|
@ -747,6 +749,81 @@ const conversationsSlice = createSlice({
|
|||
state.mentionMembers = action.payload;
|
||||
return state;
|
||||
},
|
||||
incomingCall(state: ConversationsStateType, action: PayloadAction<{ pubkey: string }>) {
|
||||
const callerPubkey = action.payload.pubkey;
|
||||
const existingCallState = state.conversationLookup[callerPubkey].callState;
|
||||
if (existingCallState !== undefined && existingCallState !== 'none') {
|
||||
return state;
|
||||
}
|
||||
const foundConvo = getConversationController().get(callerPubkey);
|
||||
if (!foundConvo) {
|
||||
return state;
|
||||
}
|
||||
|
||||
// we have to update the model itself.
|
||||
// not the db (as we dont want to store that field in it)
|
||||
// and not the redux store directly as it gets overriden by the commit() of the conversationModel
|
||||
foundConvo.callState = 'incoming';
|
||||
|
||||
void foundConvo.commit();
|
||||
return state;
|
||||
},
|
||||
endCall(state: ConversationsStateType, action: PayloadAction<{ pubkey: string }>) {
|
||||
const callerPubkey = action.payload.pubkey;
|
||||
const existingCallState = state.conversationLookup[callerPubkey].callState;
|
||||
if (!existingCallState || existingCallState === 'none') {
|
||||
return state;
|
||||
}
|
||||
|
||||
const foundConvo = getConversationController().get(callerPubkey);
|
||||
if (!foundConvo) {
|
||||
return state;
|
||||
}
|
||||
|
||||
// we have to update the model itself.
|
||||
// not the db (as we dont want to store that field in it)
|
||||
// and not the redux store directly as it gets overriden by the commit() of the conversationModel
|
||||
foundConvo.callState = 'none';
|
||||
|
||||
void foundConvo.commit();
|
||||
return state;
|
||||
},
|
||||
answerCall(state: ConversationsStateType, action: PayloadAction<{ pubkey: string }>) {
|
||||
const callerPubkey = action.payload.pubkey;
|
||||
const existingCallState = state.conversationLookup[callerPubkey].callState;
|
||||
if (!existingCallState || existingCallState !== 'incoming') {
|
||||
return state;
|
||||
}
|
||||
const foundConvo = getConversationController().get(callerPubkey);
|
||||
if (!foundConvo) {
|
||||
return state;
|
||||
}
|
||||
|
||||
// we have to update the model itself.
|
||||
// not the db (as we dont want to store that field in it)
|
||||
// and not the redux store directly as it gets overriden by the commit() of the conversationModel
|
||||
|
||||
foundConvo.callState = 'connecting';
|
||||
void foundConvo.commit();
|
||||
return state;
|
||||
},
|
||||
callConnected(state: ConversationsStateType, action: PayloadAction<{ pubkey: string }>) {
|
||||
const callerPubkey = action.payload.pubkey;
|
||||
const existingCallState = state.conversationLookup[callerPubkey].callState;
|
||||
if (!existingCallState || existingCallState === 'ongoing') {
|
||||
return state;
|
||||
}
|
||||
const foundConvo = getConversationController().get(callerPubkey);
|
||||
if (!foundConvo) {
|
||||
return state;
|
||||
}
|
||||
// we have to update the model itself.
|
||||
// not the db (as we dont want to store that field in it)
|
||||
// and not the redux store directly as it gets overriden by the commit() of the conversationModel
|
||||
foundConvo.callState = 'ongoing';
|
||||
void foundConvo.commit();
|
||||
return state;
|
||||
},
|
||||
},
|
||||
extraReducers: (builder: any) => {
|
||||
// Add reducers for additional action types here, and handle loading state as needed
|
||||
|
@ -806,6 +883,11 @@ export const {
|
|||
quotedMessageToAnimate,
|
||||
setNextMessageToPlayId,
|
||||
updateMentionsMembers,
|
||||
// calls
|
||||
incomingCall,
|
||||
endCall,
|
||||
answerCall,
|
||||
callConnected,
|
||||
} = actions;
|
||||
|
||||
export async function openConversationWithMessages(args: {
|
||||
|
|
|
@ -66,6 +66,47 @@ export const getSelectedConversation = createSelector(
|
|||
}
|
||||
);
|
||||
|
||||
export const getHasIncomingCallFrom = createSelector(
|
||||
getConversations,
|
||||
(state: ConversationsStateType): ReduxConversationType | undefined => {
|
||||
const foundEntry = Object.entries(state.conversationLookup).find(
|
||||
([_convoKey, convo]) => convo.callState === 'incoming'
|
||||
);
|
||||
|
||||
if (!foundEntry) {
|
||||
return undefined;
|
||||
}
|
||||
return foundEntry[1];
|
||||
}
|
||||
);
|
||||
|
||||
export const getHasOngoingCallWith = createSelector(
|
||||
getConversations,
|
||||
(state: ConversationsStateType): ReduxConversationType | undefined => {
|
||||
const foundEntry = Object.entries(state.conversationLookup).find(
|
||||
([_convoKey, convo]) =>
|
||||
convo.callState === 'connecting' ||
|
||||
convo.callState === 'offering' ||
|
||||
convo.callState === 'ongoing'
|
||||
);
|
||||
|
||||
if (!foundEntry) {
|
||||
return undefined;
|
||||
}
|
||||
return foundEntry[1];
|
||||
}
|
||||
);
|
||||
|
||||
export const getHasIncomingCall = createSelector(
|
||||
getHasIncomingCallFrom,
|
||||
(withConvo: ReduxConversationType | undefined): boolean => !!withConvo
|
||||
);
|
||||
|
||||
export const getHasOngoingCall = createSelector(
|
||||
getHasOngoingCallWith,
|
||||
(withConvo: ReduxConversationType | undefined): boolean => !!withConvo
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns true if the current conversation selected is a group conversation.
|
||||
* Returns false if the current conversation selected is not a group conversation, or none are selected
|
||||
|
|
Loading…
Reference in New Issue