add a button to start a video call

pull/1993/head
Audric Ackermann 3 years ago
parent ef25e5956f
commit cca1b4dabe
No known key found for this signature in database
GPG Key ID: 999F434D76324AD4

@ -447,11 +447,11 @@
"unableToCallTitle": "Cannot start new call",
"callMissed": "Missed call from $name$",
"callMissedTitle": "Call missed",
"startVideoCall": "Start Video Call",
"noCameraFound": "No camera found",
"noAudioInputFound": "No audio input found",
"callMediaPermissionsTitle": "Voice and video calls",
"callMissedCausePermission": "Call missed from '$name$' because you need to enable the 'Voice and video calls' permission in the Privacy Settings.",
"callMediaPermissionsDescription": "Allows access to accept voice and video calls from other users",
"callMediaPermissionsDialogContent": "The current implementation of voice/video calls will expose your IP address to the Oxen Foundation servers and the calling/called user."
"callMediaPermissionsDialogContent": "The current implementation of voice/video calls will expose your IP address to the Oxen Foundation servers and the calling/called user.",
"menuCall": "Call"
}

@ -14,6 +14,10 @@ import {
getConversationHeaderProps,
getConversationHeaderTitleProps,
getCurrentNotificationSettingText,
getHasIncomingCall,
getHasOngoingCall,
getIsSelectedNoteToSelf,
getIsSelectedPrivate,
getSelectedConversation,
getSelectedConversationIsPublic,
getSelectedConversationKey,
@ -35,6 +39,7 @@ import {
openRightPanel,
resetSelectedMessageIds,
} from '../../state/ducks/conversations';
import { callRecipient } from '../../interactions/conversationInteractions';
export interface TimerOption {
name: string;
@ -202,6 +207,32 @@ const BackButton = (props: { onGoBack: () => void; showBackButton: boolean }) =>
);
};
const CallButton = () => {
const isPrivate = useSelector(getIsSelectedPrivate);
const isMe = useSelector(getIsSelectedNoteToSelf);
const selectedConvoKey = useSelector(getSelectedConversationKey);
const hasIncomingCall = useSelector(getHasIncomingCall);
const hasOngoingCall = useSelector(getHasOngoingCall);
const canCall = !(hasIncomingCall || hasOngoingCall);
if (!isPrivate || isMe || !selectedConvoKey) {
return null;
}
return (
<SessionIconButton
iconType="phone"
iconSize="large"
iconPadding="2px"
margin="0 10px 0 0"
onClick={() => {
void callRecipient(selectedConvoKey, canCall);
}}
/>
);
};
export const StyledSubtitleContainer = styled.div`
display: flex;
flex-direction: row;
@ -362,17 +393,20 @@ export const ConversationHeaderWithDetails = () => {
{!isKickedFromGroup && <ExpirationLength expirationSettingName={expirationSettingName} />}
{!isSelectionMode && (
<AvatarHeader
onAvatarClick={() => {
dispatch(openRightPanel());
}}
pubkey={conversationKey}
showBackButton={isMessageDetailOpened}
avatarPath={avatarPath}
memberAvatars={memberDetails}
name={name}
profileName={profileName}
/>
<>
<CallButton />
<AvatarHeader
onAvatarClick={() => {
dispatch(openRightPanel());
}}
pubkey={conversationKey}
showBackButton={isMessageDetailOpened}
avatarPath={avatarPath}
memberAvatars={memberDetails}
name={name}
profileName={profileName}
/>
</>
)}
<MemoConversationHeaderMenu

@ -118,7 +118,7 @@ const AudioInputMenu = ({
);
};
export const ShowInFullScreenButton = ({ isFullScreen }: { isFullScreen: boolean }) => {
const ShowInFullScreenButton = ({ isFullScreen }: { isFullScreen: boolean }) => {
const dispatch = useDispatch();
const showInFullScreen = () => {
@ -270,7 +270,8 @@ export const CallWindowControls = ({
}) => {
return (
<StyledCallWindowControls>
<HangUpButton />
{!remoteStreamVideoIsMuted && <ShowInFullScreenButton isFullScreen={isFullScreen} />}
<VideoInputButton
currentConnectedCameras={currentConnectedCameras}
localStreamVideoIsMuted={localStreamVideoIsMuted}
@ -281,8 +282,7 @@ export const CallWindowControls = ({
isAudioMuted={isAudioMuted}
hideArrowIcon={isFullScreen}
/>
{!remoteStreamVideoIsMuted && <ShowInFullScreenButton isFullScreen={isFullScreen} />}
<HangUpButton />
</StyledCallWindowControls>
);
};

@ -34,6 +34,7 @@ export type SessionIconType =
| 'oxen'
| 'pause'
| 'pencil'
| 'phone'
| 'pin'
| 'play'
| 'plus'
@ -281,6 +282,12 @@ export const icons = {
viewBox: '1 1 21 21',
ratio: 1,
},
phone: {
path:
'M2.8,180.875c46.6,134,144.7,286.2,282.9,424.399c138.2,138.2,290.4,236.301,424.4,282.9c18.2,6.3,38.3,1.8,52-11.8 l92.7-92.7l21.6-21.6c19.5-19.5,19.5-51.2,0-70.7l-143.5-143.4c-19.5-19.5-51.2-19.5-70.7,0l-38.899,38.9 c-20.2,20.2-52.4,22.2-75,4.6c-44.7-34.8-89-73.899-131.9-116.8c-42.9-42.9-82-87.2-116.8-131.9c-17.601-22.6-15.601-54.7,4.6-75 l38.9-38.9c19.5-19.5,19.5-51.2,0-70.7l-143.5-143.5c-19.5-19.5-51.2-19.5-70.7,0l-21.6,21.6l-92.7,92.7 C1,142.575-3.5,162.675,2.8,180.875z',
viewBox: '0 0 891.024 891.024',
ratio: 1,
},
pin: {
path:
'M83.88.451L122.427 39c.603.601.603 1.585 0 2.188l-13.128 13.125c-.602.604-1.586.604-2.187 0l-3.732-3.73-17.303 17.3c3.882 14.621.095 30.857-11.37 42.32-.266.268-.535.529-.808.787-1.004.955-.843.949-1.813-.021L47.597 86.48 0 122.867l36.399-47.584L11.874 50.76c-.978-.98-.896-.826.066-1.837.24-.251.485-.503.734-.753C24.137 36.707 40.376 32.917 54.996 36.8l17.301-17.3-3.733-3.732c-.601-.601-.601-1.585 0-2.188L81.691.451c.604-.601 1.588-.601 2.189 0z',

@ -21,6 +21,7 @@ import { SectionType } from '../../../state/ducks/section';
import { getConversationController } from '../../../session/conversations';
import {
blockConvoById,
callRecipient,
clearNickNameByConvoId,
copyPublicKeyByConvoId,
deleteAllMessagesByConvoIdWithConfirmation,
@ -36,8 +37,7 @@ import {
} from '../../../interactions/conversationInteractions';
import { SessionButtonColor } from '../SessionButton';
import { getTimerOptions } from '../../../state/selectors/timerOptions';
import { CallManager, ToastUtils } from '../../../session/utils';
import { getCallMediaPermissionsSettings } from '../settings/SessionSettings';
import { ToastUtils } from '../../../session/utils';
const maxNumberOfPinnedConversations = 5;
@ -357,34 +357,17 @@ export function getStartCallMenuItem(conversationId: string): JSX.Element | null
const hasIncomingCall = useSelector(getHasIncomingCall);
const hasOngoingCall = useSelector(getHasOngoingCall);
const canCall = !(hasIncomingCall || hasOngoingCall);
if (!convoOut?.isPrivate()) {
if (!convoOut?.isPrivate() || convoOut.isMe()) {
return null;
}
return (
<Item
onClick={async () => {
// TODO: either pass param to callRecipient or call different call methods based on item selected.
// TODO: one time redux-persisted permission modal?
const convo = getConversationController().get(conversationId);
if (!canCall) {
ToastUtils.pushUnableToCall();
return;
}
if (!getCallMediaPermissionsSettings()) {
ToastUtils.pushVideoCallPermissionNeeded();
return;
}
if (convo) {
convo.callState = 'offering';
await convo.commit();
await CallManager.USER_callRecipient(convo.id);
}
onClick={() => {
void callRecipient(conversationId, canCall);
}}
>
{'Video Call'}
{window.i18n('menuCall')}
</Item>
);
}

@ -4,7 +4,7 @@ import {
openGroupV2ConversationIdRegex,
} from '../opengroup/utils/OpenGroupUtils';
import { getV2OpenGroupRoom } from '../data/opengroups';
import { SyncUtils, ToastUtils, UserUtils } from '../session/utils';
import { CallManager, SyncUtils, ToastUtils, UserUtils } from '../session/utils';
import { ConversationNotificationSettingType, ConversationTypeEnum } from '../models/conversation';
import _ from 'lodash';
@ -35,6 +35,7 @@ import { FSv2 } from '../fileserver';
import { fromHexToArray, toHex } from '../session/utils/String';
import { SessionButtonColor } from '../components/session/SessionButton';
import { perfEnd, perfStart } from '../session/utils/Performance';
import { getCallMediaPermissionsSettings } from '../components/session/settings/SessionSettings';
export const getCompleteUrlForV2ConvoId = async (convoId: string) => {
if (convoId.match(openGroupV2ConversationIdRegex)) {
@ -431,3 +432,23 @@ function isURL(str: string) {
const url = new RegExp(urlRegex, 'i');
return str.length < 2083 && url.test(str);
}
export async function callRecipient(pubkey: string, canCall: boolean) {
const convo = getConversationController().get(pubkey);
if (!canCall) {
ToastUtils.pushUnableToCall();
return;
}
if (!getCallMediaPermissionsSettings()) {
ToastUtils.pushVideoCallPermissionNeeded();
return;
}
if (convo && convo.isPrivate() && !convo.isMe()) {
convo.callState = 'offering';
await convo.commit();
await CallManager.USER_callRecipient(convo.id);
}
}

@ -590,6 +590,20 @@ export const getConversationHeaderProps = createSelector(getSelectedConversation
};
});
export const getIsSelectedPrivate = createSelector(
getConversationHeaderProps,
(headerProps): boolean => {
return headerProps?.isPrivate || false;
}
);
export const getIsSelectedNoteToSelf = createSelector(
getConversationHeaderProps,
(headerProps): boolean => {
return headerProps?.isMe || false;
}
);
export const getNumberOfPinnedConversations = createSelector(getConversations, (state): number => {
const values = Object.values(state.conversationLookup);
return values.filter(conversation => conversation.isPinned).length;

Loading…
Cancel
Save