From 1a699879cf7931af632a43ac73baec763cd26067 Mon Sep 17 00:00:00 2001 From: Audric Ackermann Date: Thu, 2 Dec 2021 11:13:47 +1100 Subject: [PATCH] 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 --- js/background.js | 2 +- main.js | 6 +-- preload.js | 18 ------- ts/components/conversation/Timestamp.tsx | 2 +- .../session/SessionClosableOverlay.tsx | 2 +- .../calling/InConversationCallContainer.tsx | 44 +++++++++++++---- .../settings/section/CategoryPrivacy.tsx | 21 +++++---- ts/session/utils/calling/CallManager.ts | 24 +++++++++- ts/state/selectors/call.ts | 47 ++++++++++--------- 9 files changed, 102 insertions(+), 64 deletions(-) diff --git a/js/background.js b/js/background.js index bbf003ee3..5fae82823 100644 --- a/js/background.js +++ b/js/background.js @@ -427,7 +427,7 @@ Whisper.Notifications.disable(); // avoid notification flood until empty setTimeout(() => { Whisper.Notifications.enable(); - }, window.CONSTANTS.NOTIFICATION_ENABLE_TIMEOUT_SECONDS * 1000); + }, 10 * 1000); // 10 sec window.NewReceiver.queueAllCached(); window.libsession.Utils.AttachmentDownloads.start({ diff --git a/main.js b/main.js index c0cdc9c5f..307069835 100644 --- a/main.js +++ b/main.js @@ -246,7 +246,7 @@ async function createWindow() { minWidth, minHeight, autoHideMenuBar: false, - backgroundColor: '#fff', + backgroundColor: '#000', webPreferences: { nodeIntegration: false, enableRemoteModule: true, @@ -535,7 +535,7 @@ function showAbout() { resizable: false, title: locale.messages.about, autoHideMenuBar: true, - backgroundColor: '#ffffff', + backgroundColor: '#000', show: false, webPreferences: { nodeIntegration: false, @@ -577,7 +577,7 @@ async function showDebugLogWindow() { resizable: false, title: locale.messages.debugLog, autoHideMenuBar: true, - backgroundColor: '#FFFFFF', + backgroundColor: '#000', show: false, modal: true, webPreferences: { diff --git a/preload.js b/preload.js index cd6fa895e..e53586ed7 100644 --- a/preload.js +++ b/preload.js @@ -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 = { environment: window.getEnvironment(), version: window.getVersion(), @@ -270,9 +264,7 @@ window.moment.updateLocale(localeSetForMoment, { }); window.libsession = require('./ts/session'); -window.models = require('./ts/models'); -window.Signal = window.Signal || {}; window.Signal.Data = require('./ts/data/data'); window.Signal.Logs = require('./js/modules/logs'); @@ -287,16 +279,6 @@ window.addEventListener('contextmenu', e => { }); 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 diff --git a/ts/components/conversation/Timestamp.tsx b/ts/components/conversation/Timestamp.tsx index e5d61204e..e277062f8 100644 --- a/ts/components/conversation/Timestamp.tsx +++ b/ts/components/conversation/Timestamp.tsx @@ -55,7 +55,7 @@ export const Timestamp = (props: Props) => { // Use relative time for under 24hrs ago. 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; let dateString; diff --git a/ts/components/session/SessionClosableOverlay.tsx b/ts/components/session/SessionClosableOverlay.tsx index eddc9450f..1c280a2c9 100644 --- a/ts/components/session/SessionClosableOverlay.tsx +++ b/ts/components/session/SessionClosableOverlay.tsx @@ -177,7 +177,7 @@ export class SessionClosableOverlay extends React.Component { placeholder={placeholder} value={groupName} isGroup={true} - maxLength={window.CONSTANTS.MAX_GROUPNAME_LENGTH} + maxLength={100} onChange={this.onGroupNameChanged} onPressEnter={() => onButtonClick(groupName, selectedMembers)} /> diff --git a/ts/components/session/calling/InConversationCallContainer.tsx b/ts/components/session/calling/InConversationCallContainer.tsx index e0b5f1d67..5069d01b3 100644 --- a/ts/components/session/calling/InConversationCallContainer.tsx +++ b/ts/components/session/calling/InConversationCallContainer.tsx @@ -1,14 +1,15 @@ -import React, { useRef } from 'react'; import { useSelector } from 'react-redux'; +import React, { useRef, useState } from 'react'; import styled from 'styled-components'; import _ from 'underscore'; -import { UserUtils } from '../../../session/utils'; +import { CallManager, UserUtils } from '../../../session/utils'; import { getCallIsInFullScreen, + getCallWithFocusedConvoIsOffering, + getCallWithFocusedConvosIsConnected, + getCallWithFocusedConvosIsConnecting, getHasOngoingCallWithFocusedConvo, - getHasOngoingCallWithFocusedConvoIsOffering, - getHasOngoingCallWithFocusedConvosIsConnecting, getHasOngoingCallWithPubkey, } from '../../../state/selectors/call'; import { StyledVideoElement } from './DraggableCallContainer'; @@ -19,11 +20,15 @@ import { useModuloWithTripleDots } from '../../../hooks/useModuloWithTripleDots' import { CallWindowControls } from './CallButtons'; import { SessionSpinner } from '../SessionSpinner'; 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` height: 100%; width: 50%; z-index: 0; + padding-top: 30px; // leave some space at the top for the connecting/duration of the current call `; const InConvoCallWindow = styled.div` @@ -66,10 +71,11 @@ const StyledCenteredLabel = styled.div` white-space: nowrap; color: white; text-shadow: 0px 0px 8px white; + z-index: 5; `; const RingingLabel = () => { - const ongoingCallWithFocusedIsRinging = useSelector(getHasOngoingCallWithFocusedConvoIsOffering); + const ongoingCallWithFocusedIsRinging = useSelector(getCallWithFocusedConvoIsOffering); const modulatedStr = useModuloWithTripleDots(window.i18n('ringing'), 3, 1000); if (!ongoingCallWithFocusedIsRinging) { @@ -79,9 +85,7 @@ const RingingLabel = () => { }; const ConnectingLabel = () => { - const ongoingCallWithFocusedIsConnecting = useSelector( - getHasOngoingCallWithFocusedConvosIsConnecting - ); + const ongoingCallWithFocusedIsConnecting = useSelector(getCallWithFocusedConvosIsConnecting); const modulatedStr = useModuloWithTripleDots(window.i18n('establishingConnection'), 3, 1000); @@ -92,6 +96,29 @@ const ConnectingLabel = () => { return {modulatedStr}; }; +const DurationLabel = () => { + const [callDuration, setCallDuration] = useState(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 {dateString}; +}; + const StyledSpinner = styled.div<{ fullWidth: boolean }>` height: 100%; width: ${props => (props.fullWidth ? '100%' : '50%')}; @@ -167,6 +194,7 @@ export const InConversationCallContainer = () => { + @@ -71,7 +73,6 @@ export const SettingsCategoryPrivacy = (props: { description={window.i18n('mediaPermissionsDescription')} active={Boolean(window.getSettingValue('media-permissions'))} /> - {window.lokiFeatureFlags.useCallMessage && ( { @@ -113,14 +114,16 @@ export const SettingsCategoryPrivacy = (props: { description={window.i18n('autoUpdateSettingDescription')} active={Boolean(window.getSettingValue(settingsAutoUpdate))} /> - { - dispatch(toggleMessageRequests()); - }} - title={window.i18n('messageRequests')} - description={window.i18n('messageRequestsDescription')} - active={useSelector(getIsMessageRequestsEnabled)} - /> + {hasMessageRequestFlag && ( + { + dispatch(toggleMessageRequests()); + }} + title={window.i18n('messageRequests')} + description={window.i18n('messageRequestsDescription')} + active={useSelector(getIsMessageRequestsEnabled)} + /> + )} {!props.hasPassword && ( = new Set(); export type CallManagerOptionsType = { @@ -591,12 +593,16 @@ function handleConnectionStateChanged(pubkey: string) { if (firstAudioOutput) { void selectAudioOutputByDeviceId(firstAudioOutput); } + + currentCallStartTimestamp = Date.now(); + window.inboxStore?.dispatch(callConnected({ pubkey })); } } function closeVideoCall() { window.log.info('closingVideoCall '); + currentCallStartTimestamp = undefined; setIsRinging(false); if (peerConnection) { peerConnection.ontrack = null; @@ -909,13 +915,13 @@ export async function handleCallTypeEndCall(sender: string, aboutCallUUID?: stri if (aboutCallUUID) { rejectedCallUUIDS.add(aboutCallUUID); + const { ongoingCallStatus, ongoingCallWith } = getCallingStateOutsideOfRedux(); clearCallCacheFromPubkeyAndUUID(sender, aboutCallUUID); // 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 (sender === UserUtils.getOurPubKeyStrFromCache()) { - const { ongoingCallStatus, ongoingCallWith } = getCallingStateOutsideOfRedux(); const ownerOfCall = getOwnerOfCallUUID(aboutCallUUID); if ( @@ -928,8 +934,17 @@ export async function handleCallTypeEndCall(sender: string, aboutCallUUID?: stri return; } + // remote user hangup while we were on the call with him if (aboutCallUUID === currentCallUUID) { 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()); } } @@ -1295,8 +1310,15 @@ export function onTurnedOnCallMediaPermissions() { Date.now() - msg.timestamp < DURATION.MINUTES * 1 ) { window.inboxStore?.dispatch(incomingCall({ pubkey: key })); + break; } } }); }); } + +export function getCurrentCallDuration() { + return currentCallStartTimestamp + ? Math.floor((Date.now() - currentCallStartTimestamp) / 1000) + : undefined; +} diff --git a/ts/state/selectors/call.ts b/ts/state/selectors/call.ts index 819938245..788f4f6e1 100644 --- a/ts/state/selectors/call.ts +++ b/ts/state/selectors/call.ts @@ -1,5 +1,5 @@ import { createSelector } from 'reselect'; -import { CallStateType } from '../ducks/call'; +import { CallStateType, CallStatusEnum } from '../ducks/call'; import { ConversationsStateType, ReduxConversationType } from '../ducks/conversations'; import { StateType } from '../reducer'; import { getConversations, getSelectedConversationKey } from './conversations'; @@ -55,37 +55,40 @@ export const getHasOngoingCallWithFocusedConvo = createSelector( } ); -export const getHasOngoingCallWithFocusedConvoIsOffering = createSelector( +const getCallStateWithFocusedConvo = createSelector( getCallState, getSelectedConversationKey, - (callState: CallStateType, selectedConvoPubkey?: string): boolean => { + (callState: CallStateType, selectedConvoPubkey?: string): CallStatusEnum => { if ( - !selectedConvoPubkey || - !callState.ongoingWith || - callState.ongoingCallStatus !== 'offering' || - selectedConvoPubkey !== callState.ongoingWith + selectedConvoPubkey && + callState.ongoingWith && + selectedConvoPubkey === callState.ongoingWith ) { - return false; + return callState.ongoingCallStatus; } - return true; + return undefined; } ); -export const getHasOngoingCallWithFocusedConvosIsConnecting = createSelector( - getCallState, - getSelectedConversationKey, - (callState: CallStateType, selectedConvoPubkey?: string): boolean => { - if ( - !selectedConvoPubkey || - !callState.ongoingWith || - callState.ongoingCallStatus !== 'connecting' || - selectedConvoPubkey !== callState.ongoingWith - ) { - return false; - } +export const getCallWithFocusedConvoIsOffering = createSelector( + getCallStateWithFocusedConvo, + (callState: CallStatusEnum): boolean => { + return callState === 'offering'; + } +); + +export const getCallWithFocusedConvosIsConnecting = createSelector( + getCallStateWithFocusedConvo, + (callState: CallStatusEnum): boolean => { + return callState === 'connecting'; + } +); - return true; +export const getCallWithFocusedConvosIsConnected = createSelector( + getCallStateWithFocusedConvo, + (callState: CallStatusEnum): boolean => { + return callState === 'ongoing'; } );