Add call duration (#2059)

* add call duration once connected

* close incoming call dialog if endCall from same sender

* disable message request toggle if featureFlag is OFF
pull/2071/head
Audric Ackermann 3 years ago committed by GitHub
parent 1ec637b551
commit 1a699879cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -427,7 +427,7 @@
Whisper.Notifications.disable(); // avoid notification flood until empty Whisper.Notifications.disable(); // avoid notification flood until empty
setTimeout(() => { setTimeout(() => {
Whisper.Notifications.enable(); Whisper.Notifications.enable();
}, window.CONSTANTS.NOTIFICATION_ENABLE_TIMEOUT_SECONDS * 1000); }, 10 * 1000); // 10 sec
window.NewReceiver.queueAllCached(); window.NewReceiver.queueAllCached();
window.libsession.Utils.AttachmentDownloads.start({ window.libsession.Utils.AttachmentDownloads.start({

@ -246,7 +246,7 @@ async function createWindow() {
minWidth, minWidth,
minHeight, minHeight,
autoHideMenuBar: false, autoHideMenuBar: false,
backgroundColor: '#fff', backgroundColor: '#000',
webPreferences: { webPreferences: {
nodeIntegration: false, nodeIntegration: false,
enableRemoteModule: true, enableRemoteModule: true,
@ -535,7 +535,7 @@ function showAbout() {
resizable: false, resizable: false,
title: locale.messages.about, title: locale.messages.about,
autoHideMenuBar: true, autoHideMenuBar: true,
backgroundColor: '#ffffff', backgroundColor: '#000',
show: false, show: false,
webPreferences: { webPreferences: {
nodeIntegration: false, nodeIntegration: false,
@ -577,7 +577,7 @@ async function showDebugLogWindow() {
resizable: false, resizable: false,
title: locale.messages.debugLog, title: locale.messages.debugLog,
autoHideMenuBar: true, autoHideMenuBar: true,
backgroundColor: '#FFFFFF', backgroundColor: '#000',
show: false, show: false,
modal: true, modal: true,
webPreferences: { webPreferences: {

@ -55,12 +55,6 @@ window.isBeforeVersion = (toCheck, baseVersion) => {
} }
}; };
// eslint-disable-next-line func-names
window.CONSTANTS = new (function() {
// Number of seconds to turn on notifications after reconnect/start of app
this.NOTIFICATION_ENABLE_TIMEOUT_SECONDS = 10;
})();
window.versionInfo = { window.versionInfo = {
environment: window.getEnvironment(), environment: window.getEnvironment(),
version: window.getVersion(), version: window.getVersion(),
@ -270,9 +264,7 @@ window.moment.updateLocale(localeSetForMoment, {
}); });
window.libsession = require('./ts/session'); window.libsession = require('./ts/session');
window.models = require('./ts/models');
window.Signal = window.Signal || {};
window.Signal.Data = require('./ts/data/data'); window.Signal.Data = require('./ts/data/data');
window.Signal.Logs = require('./js/modules/logs'); window.Signal.Logs = require('./js/modules/logs');
@ -287,16 +279,6 @@ window.addEventListener('contextmenu', e => {
}); });
window.NewReceiver = require('./ts/receiver/receiver'); window.NewReceiver = require('./ts/receiver/receiver');
window.Fsv2 = require('./ts/fileserver/FileServerApiV2');
window.DataMessageReceiver = require('./ts/receiver/dataMessage');
window.NewSnodeAPI = require('./ts/session/snode_api/SNodeAPI');
window.SnodePool = require('./ts/session/snode_api/snodePool');
// eslint-disable-next-line no-extend-native,func-names
Promise.prototype.ignore = function() {
// eslint-disable-next-line more/no-then
this.then(() => {});
};
// Blocking // Blocking

@ -55,7 +55,7 @@ export const Timestamp = (props: Props) => {
// Use relative time for under 24hrs ago. // Use relative time for under 24hrs ago.
const now = Math.floor(Date.now()); const now = Math.floor(Date.now());
const messageAgeInDays = (now - timestamp) / (window.CONSTANTS.SECS_IN_DAY * 1000); const messageAgeInDays = (now - timestamp) / (1000 * 60 * 60 * 24);
const daysBeforeRelativeTiming = 1; const daysBeforeRelativeTiming = 1;
let dateString; let dateString;

@ -177,7 +177,7 @@ export class SessionClosableOverlay extends React.Component<Props, State> {
placeholder={placeholder} placeholder={placeholder}
value={groupName} value={groupName}
isGroup={true} isGroup={true}
maxLength={window.CONSTANTS.MAX_GROUPNAME_LENGTH} maxLength={100}
onChange={this.onGroupNameChanged} onChange={this.onGroupNameChanged}
onPressEnter={() => onButtonClick(groupName, selectedMembers)} onPressEnter={() => onButtonClick(groupName, selectedMembers)}
/> />

@ -1,14 +1,15 @@
import React, { useRef } from 'react';
import { useSelector } from 'react-redux'; import { useSelector } from 'react-redux';
import React, { useRef, useState } from 'react';
import styled from 'styled-components'; import styled from 'styled-components';
import _ from 'underscore'; import _ from 'underscore';
import { UserUtils } from '../../../session/utils'; import { CallManager, UserUtils } from '../../../session/utils';
import { import {
getCallIsInFullScreen, getCallIsInFullScreen,
getCallWithFocusedConvoIsOffering,
getCallWithFocusedConvosIsConnected,
getCallWithFocusedConvosIsConnecting,
getHasOngoingCallWithFocusedConvo, getHasOngoingCallWithFocusedConvo,
getHasOngoingCallWithFocusedConvoIsOffering,
getHasOngoingCallWithFocusedConvosIsConnecting,
getHasOngoingCallWithPubkey, getHasOngoingCallWithPubkey,
} from '../../../state/selectors/call'; } from '../../../state/selectors/call';
import { StyledVideoElement } from './DraggableCallContainer'; import { StyledVideoElement } from './DraggableCallContainer';
@ -19,11 +20,15 @@ import { useModuloWithTripleDots } from '../../../hooks/useModuloWithTripleDots'
import { CallWindowControls } from './CallButtons'; import { CallWindowControls } from './CallButtons';
import { SessionSpinner } from '../SessionSpinner'; import { SessionSpinner } from '../SessionSpinner';
import { DEVICE_DISABLED_DEVICE_ID } from '../../../session/utils/calling/CallManager'; import { DEVICE_DISABLED_DEVICE_ID } from '../../../session/utils/calling/CallManager';
// tslint:disable-next-line: no-submodule-imports
import useInterval from 'react-use/lib/useInterval';
import moment from 'moment';
const VideoContainer = styled.div` const VideoContainer = styled.div`
height: 100%; height: 100%;
width: 50%; width: 50%;
z-index: 0; z-index: 0;
padding-top: 30px; // leave some space at the top for the connecting/duration of the current call
`; `;
const InConvoCallWindow = styled.div` const InConvoCallWindow = styled.div`
@ -66,10 +71,11 @@ const StyledCenteredLabel = styled.div`
white-space: nowrap; white-space: nowrap;
color: white; color: white;
text-shadow: 0px 0px 8px white; text-shadow: 0px 0px 8px white;
z-index: 5;
`; `;
const RingingLabel = () => { const RingingLabel = () => {
const ongoingCallWithFocusedIsRinging = useSelector(getHasOngoingCallWithFocusedConvoIsOffering); const ongoingCallWithFocusedIsRinging = useSelector(getCallWithFocusedConvoIsOffering);
const modulatedStr = useModuloWithTripleDots(window.i18n('ringing'), 3, 1000); const modulatedStr = useModuloWithTripleDots(window.i18n('ringing'), 3, 1000);
if (!ongoingCallWithFocusedIsRinging) { if (!ongoingCallWithFocusedIsRinging) {
@ -79,9 +85,7 @@ const RingingLabel = () => {
}; };
const ConnectingLabel = () => { const ConnectingLabel = () => {
const ongoingCallWithFocusedIsConnecting = useSelector( const ongoingCallWithFocusedIsConnecting = useSelector(getCallWithFocusedConvosIsConnecting);
getHasOngoingCallWithFocusedConvosIsConnecting
);
const modulatedStr = useModuloWithTripleDots(window.i18n('establishingConnection'), 3, 1000); const modulatedStr = useModuloWithTripleDots(window.i18n('establishingConnection'), 3, 1000);
@ -92,6 +96,29 @@ const ConnectingLabel = () => {
return <StyledCenteredLabel>{modulatedStr}</StyledCenteredLabel>; return <StyledCenteredLabel>{modulatedStr}</StyledCenteredLabel>;
}; };
const DurationLabel = () => {
const [callDuration, setCallDuration] = useState<undefined | number>(undefined);
const ongoingCallWithFocusedIsConnected = useSelector(getCallWithFocusedConvosIsConnected);
useInterval(() => {
const duration = CallManager.getCurrentCallDuration();
if (duration) {
setCallDuration(duration);
}
}, 100);
if (!ongoingCallWithFocusedIsConnected || !callDuration || callDuration < 0) {
return null;
}
const ms = callDuration * 1000;
const d = moment.duration(ms);
// tslint:disable-next-line: restrict-plus-operands
const dateString = Math.floor(d.asHours()) + moment.utc(ms).format(':mm:ss');
return <StyledCenteredLabel>{dateString}</StyledCenteredLabel>;
};
const StyledSpinner = styled.div<{ fullWidth: boolean }>` const StyledSpinner = styled.div<{ fullWidth: boolean }>`
height: 100%; height: 100%;
width: ${props => (props.fullWidth ? '100%' : '50%')}; width: ${props => (props.fullWidth ? '100%' : '50%')};
@ -167,6 +194,7 @@ export const InConversationCallContainer = () => {
<RelativeCallWindow> <RelativeCallWindow>
<RingingLabel /> <RingingLabel />
<ConnectingLabel /> <ConnectingLabel />
<DurationLabel />
<VideoContainer> <VideoContainer>
<VideoLoadingSpinner fullWidth={false} /> <VideoLoadingSpinner fullWidth={false} />
<StyledVideoElement <StyledVideoElement

@ -59,6 +59,8 @@ export const SettingsCategoryPrivacy = (props: {
const forceUpdate = useUpdate(); const forceUpdate = useUpdate();
const dispatch = useDispatch(); const dispatch = useDispatch();
const hasMessageRequestFlag = window.lokiFeatureFlags.useMessageRequests;
if (props.hasPassword !== null) { if (props.hasPassword !== null) {
return ( return (
<> <>
@ -71,7 +73,6 @@ export const SettingsCategoryPrivacy = (props: {
description={window.i18n('mediaPermissionsDescription')} description={window.i18n('mediaPermissionsDescription')}
active={Boolean(window.getSettingValue('media-permissions'))} active={Boolean(window.getSettingValue('media-permissions'))}
/> />
{window.lokiFeatureFlags.useCallMessage && ( {window.lokiFeatureFlags.useCallMessage && (
<SessionToggleWithDescription <SessionToggleWithDescription
onClickToggle={async () => { onClickToggle={async () => {
@ -113,14 +114,16 @@ export const SettingsCategoryPrivacy = (props: {
description={window.i18n('autoUpdateSettingDescription')} description={window.i18n('autoUpdateSettingDescription')}
active={Boolean(window.getSettingValue(settingsAutoUpdate))} active={Boolean(window.getSettingValue(settingsAutoUpdate))}
/> />
<SessionToggleWithDescription {hasMessageRequestFlag && (
onClickToggle={() => { <SessionToggleWithDescription
dispatch(toggleMessageRequests()); onClickToggle={() => {
}} dispatch(toggleMessageRequests());
title={window.i18n('messageRequests')} }}
description={window.i18n('messageRequestsDescription')} title={window.i18n('messageRequests')}
active={useSelector(getIsMessageRequestsEnabled)} description={window.i18n('messageRequestsDescription')}
/> active={useSelector(getIsMessageRequestsEnabled)}
/>
)}
{!props.hasPassword && ( {!props.hasPassword && (
<SessionSettingButtonItem <SessionSettingButtonItem
title={window.i18n('setAccountPasswordTitle')} title={window.i18n('setAccountPasswordTitle')}

@ -37,6 +37,8 @@ export const callTimeoutMs = 30000;
*/ */
let currentCallUUID: string | undefined; let currentCallUUID: string | undefined;
let currentCallStartTimestamp: number | undefined;
const rejectedCallUUIDS: Set<string> = new Set(); const rejectedCallUUIDS: Set<string> = new Set();
export type CallManagerOptionsType = { export type CallManagerOptionsType = {
@ -591,12 +593,16 @@ function handleConnectionStateChanged(pubkey: string) {
if (firstAudioOutput) { if (firstAudioOutput) {
void selectAudioOutputByDeviceId(firstAudioOutput); void selectAudioOutputByDeviceId(firstAudioOutput);
} }
currentCallStartTimestamp = Date.now();
window.inboxStore?.dispatch(callConnected({ pubkey })); window.inboxStore?.dispatch(callConnected({ pubkey }));
} }
} }
function closeVideoCall() { function closeVideoCall() {
window.log.info('closingVideoCall '); window.log.info('closingVideoCall ');
currentCallStartTimestamp = undefined;
setIsRinging(false); setIsRinging(false);
if (peerConnection) { if (peerConnection) {
peerConnection.ontrack = null; peerConnection.ontrack = null;
@ -909,13 +915,13 @@ export async function handleCallTypeEndCall(sender: string, aboutCallUUID?: stri
if (aboutCallUUID) { if (aboutCallUUID) {
rejectedCallUUIDS.add(aboutCallUUID); rejectedCallUUIDS.add(aboutCallUUID);
const { ongoingCallStatus, ongoingCallWith } = getCallingStateOutsideOfRedux();
clearCallCacheFromPubkeyAndUUID(sender, aboutCallUUID); clearCallCacheFromPubkeyAndUUID(sender, aboutCallUUID);
// this is a end call from ourself. We must remove the popup about the incoming call // this is a end call from ourself. We must remove the popup about the incoming call
// if it matches the owner of this callUUID // if it matches the owner of this callUUID
if (sender === UserUtils.getOurPubKeyStrFromCache()) { if (sender === UserUtils.getOurPubKeyStrFromCache()) {
const { ongoingCallStatus, ongoingCallWith } = getCallingStateOutsideOfRedux();
const ownerOfCall = getOwnerOfCallUUID(aboutCallUUID); const ownerOfCall = getOwnerOfCallUUID(aboutCallUUID);
if ( if (
@ -928,8 +934,17 @@ export async function handleCallTypeEndCall(sender: string, aboutCallUUID?: stri
return; return;
} }
// remote user hangup while we were on the call with him
if (aboutCallUUID === currentCallUUID) { if (aboutCallUUID === currentCallUUID) {
closeVideoCall(); closeVideoCall();
window.inboxStore?.dispatch(endCall());
} else if (
ongoingCallWith === sender &&
(ongoingCallStatus === 'incoming' || ongoingCallStatus === 'connecting')
) {
// remote user hangup an offer he sent but we did not accept it yet
setIsRinging(false);
window.inboxStore?.dispatch(endCall()); window.inboxStore?.dispatch(endCall());
} }
} }
@ -1295,8 +1310,15 @@ export function onTurnedOnCallMediaPermissions() {
Date.now() - msg.timestamp < DURATION.MINUTES * 1 Date.now() - msg.timestamp < DURATION.MINUTES * 1
) { ) {
window.inboxStore?.dispatch(incomingCall({ pubkey: key })); window.inboxStore?.dispatch(incomingCall({ pubkey: key }));
break;
} }
} }
}); });
}); });
} }
export function getCurrentCallDuration() {
return currentCallStartTimestamp
? Math.floor((Date.now() - currentCallStartTimestamp) / 1000)
: undefined;
}

@ -1,5 +1,5 @@
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import { CallStateType } from '../ducks/call'; import { CallStateType, CallStatusEnum } from '../ducks/call';
import { ConversationsStateType, ReduxConversationType } from '../ducks/conversations'; import { ConversationsStateType, ReduxConversationType } from '../ducks/conversations';
import { StateType } from '../reducer'; import { StateType } from '../reducer';
import { getConversations, getSelectedConversationKey } from './conversations'; import { getConversations, getSelectedConversationKey } from './conversations';
@ -55,37 +55,40 @@ export const getHasOngoingCallWithFocusedConvo = createSelector(
} }
); );
export const getHasOngoingCallWithFocusedConvoIsOffering = createSelector( const getCallStateWithFocusedConvo = createSelector(
getCallState, getCallState,
getSelectedConversationKey, getSelectedConversationKey,
(callState: CallStateType, selectedConvoPubkey?: string): boolean => { (callState: CallStateType, selectedConvoPubkey?: string): CallStatusEnum => {
if ( if (
!selectedConvoPubkey || selectedConvoPubkey &&
!callState.ongoingWith || callState.ongoingWith &&
callState.ongoingCallStatus !== 'offering' || selectedConvoPubkey === callState.ongoingWith
selectedConvoPubkey !== callState.ongoingWith
) { ) {
return false; return callState.ongoingCallStatus;
} }
return true; return undefined;
} }
); );
export const getHasOngoingCallWithFocusedConvosIsConnecting = createSelector( export const getCallWithFocusedConvoIsOffering = createSelector(
getCallState, getCallStateWithFocusedConvo,
getSelectedConversationKey, (callState: CallStatusEnum): boolean => {
(callState: CallStateType, selectedConvoPubkey?: string): boolean => { return callState === 'offering';
if ( }
!selectedConvoPubkey || );
!callState.ongoingWith ||
callState.ongoingCallStatus !== 'connecting' || export const getCallWithFocusedConvosIsConnecting = createSelector(
selectedConvoPubkey !== callState.ongoingWith getCallStateWithFocusedConvo,
) { (callState: CallStatusEnum): boolean => {
return false; return callState === 'connecting';
} }
);
return true; export const getCallWithFocusedConvosIsConnected = createSelector(
getCallStateWithFocusedConvo,
(callState: CallStatusEnum): boolean => {
return callState === 'ongoing';
} }
); );

Loading…
Cancel
Save