add a button to start a video call

pull/1993/head
Audric Ackermann 4 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", "unableToCallTitle": "Cannot start new call",
"callMissed": "Missed call from $name$", "callMissed": "Missed call from $name$",
"callMissedTitle": "Call missed", "callMissedTitle": "Call missed",
"startVideoCall": "Start Video Call",
"noCameraFound": "No camera found", "noCameraFound": "No camera found",
"noAudioInputFound": "No audio input found", "noAudioInputFound": "No audio input found",
"callMediaPermissionsTitle": "Voice and video calls", "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.", "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", "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, getConversationHeaderProps,
getConversationHeaderTitleProps, getConversationHeaderTitleProps,
getCurrentNotificationSettingText, getCurrentNotificationSettingText,
getHasIncomingCall,
getHasOngoingCall,
getIsSelectedNoteToSelf,
getIsSelectedPrivate,
getSelectedConversation, getSelectedConversation,
getSelectedConversationIsPublic, getSelectedConversationIsPublic,
getSelectedConversationKey, getSelectedConversationKey,
@ -35,6 +39,7 @@ import {
openRightPanel, openRightPanel,
resetSelectedMessageIds, resetSelectedMessageIds,
} from '../../state/ducks/conversations'; } from '../../state/ducks/conversations';
import { callRecipient } from '../../interactions/conversationInteractions';
export interface TimerOption { export interface TimerOption {
name: string; 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` export const StyledSubtitleContainer = styled.div`
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -362,6 +393,8 @@ export const ConversationHeaderWithDetails = () => {
{!isKickedFromGroup && <ExpirationLength expirationSettingName={expirationSettingName} />} {!isKickedFromGroup && <ExpirationLength expirationSettingName={expirationSettingName} />}
{!isSelectionMode && ( {!isSelectionMode && (
<>
<CallButton />
<AvatarHeader <AvatarHeader
onAvatarClick={() => { onAvatarClick={() => {
dispatch(openRightPanel()); dispatch(openRightPanel());
@ -373,6 +406,7 @@ export const ConversationHeaderWithDetails = () => {
name={name} name={name}
profileName={profileName} profileName={profileName}
/> />
</>
)} )}
<MemoConversationHeaderMenu <MemoConversationHeaderMenu

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

@ -34,6 +34,7 @@ export type SessionIconType =
| 'oxen' | 'oxen'
| 'pause' | 'pause'
| 'pencil' | 'pencil'
| 'phone'
| 'pin' | 'pin'
| 'play' | 'play'
| 'plus' | 'plus'
@ -281,6 +282,12 @@ export const icons = {
viewBox: '1 1 21 21', viewBox: '1 1 21 21',
ratio: 1, 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: { pin: {
path: 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', '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 { getConversationController } from '../../../session/conversations';
import { import {
blockConvoById, blockConvoById,
callRecipient,
clearNickNameByConvoId, clearNickNameByConvoId,
copyPublicKeyByConvoId, copyPublicKeyByConvoId,
deleteAllMessagesByConvoIdWithConfirmation, deleteAllMessagesByConvoIdWithConfirmation,
@ -36,8 +37,7 @@ import {
} from '../../../interactions/conversationInteractions'; } from '../../../interactions/conversationInteractions';
import { SessionButtonColor } from '../SessionButton'; import { SessionButtonColor } from '../SessionButton';
import { getTimerOptions } from '../../../state/selectors/timerOptions'; import { getTimerOptions } from '../../../state/selectors/timerOptions';
import { CallManager, ToastUtils } from '../../../session/utils'; import { ToastUtils } from '../../../session/utils';
import { getCallMediaPermissionsSettings } from '../settings/SessionSettings';
const maxNumberOfPinnedConversations = 5; const maxNumberOfPinnedConversations = 5;
@ -357,34 +357,17 @@ export function getStartCallMenuItem(conversationId: string): JSX.Element | null
const hasIncomingCall = useSelector(getHasIncomingCall); const hasIncomingCall = useSelector(getHasIncomingCall);
const hasOngoingCall = useSelector(getHasOngoingCall); const hasOngoingCall = useSelector(getHasOngoingCall);
const canCall = !(hasIncomingCall || hasOngoingCall); const canCall = !(hasIncomingCall || hasOngoingCall);
if (!convoOut?.isPrivate()) { if (!convoOut?.isPrivate() || convoOut.isMe()) {
return null; return null;
} }
return ( return (
<Item <Item
onClick={async () => { onClick={() => {
// TODO: either pass param to callRecipient or call different call methods based on item selected. void callRecipient(conversationId, canCall);
// 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);
}
}} }}
> >
{'Video Call'} {window.i18n('menuCall')}
</Item> </Item>
); );
} }

@ -4,7 +4,7 @@ import {
openGroupV2ConversationIdRegex, openGroupV2ConversationIdRegex,
} from '../opengroup/utils/OpenGroupUtils'; } from '../opengroup/utils/OpenGroupUtils';
import { getV2OpenGroupRoom } from '../data/opengroups'; 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 { ConversationNotificationSettingType, ConversationTypeEnum } from '../models/conversation';
import _ from 'lodash'; import _ from 'lodash';
@ -35,6 +35,7 @@ import { FSv2 } from '../fileserver';
import { fromHexToArray, toHex } from '../session/utils/String'; import { fromHexToArray, toHex } from '../session/utils/String';
import { SessionButtonColor } from '../components/session/SessionButton'; import { SessionButtonColor } from '../components/session/SessionButton';
import { perfEnd, perfStart } from '../session/utils/Performance'; import { perfEnd, perfStart } from '../session/utils/Performance';
import { getCallMediaPermissionsSettings } from '../components/session/settings/SessionSettings';
export const getCompleteUrlForV2ConvoId = async (convoId: string) => { export const getCompleteUrlForV2ConvoId = async (convoId: string) => {
if (convoId.match(openGroupV2ConversationIdRegex)) { if (convoId.match(openGroupV2ConversationIdRegex)) {
@ -431,3 +432,23 @@ function isURL(str: string) {
const url = new RegExp(urlRegex, 'i'); const url = new RegExp(urlRegex, 'i');
return str.length < 2083 && url.test(str); 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 => { export const getNumberOfPinnedConversations = createSelector(getConversations, (state): number => {
const values = Object.values(state.conversationLookup); const values = Object.values(state.conversationLookup);
return values.filter(conversation => conversation.isPinned).length; return values.filter(conversation => conversation.isPinned).length;

Loading…
Cancel
Save