Implement lower hand suggestion in group calls
This commit is contained in:
parent
4312d03db0
commit
f2d4f669fe
11 changed files with 206 additions and 3 deletions
|
@ -3699,6 +3699,14 @@
|
||||||
"messageformat": "Mic on",
|
"messageformat": "Mic on",
|
||||||
"description": "Shown in a call when the user is muted and then unmutes their audio input using the Mute toggle button."
|
"description": "Shown in a call when the user is muted and then unmutes their audio input using the Mute toggle button."
|
||||||
},
|
},
|
||||||
|
"icu:CallControls__LowerHandSuggestionToast": {
|
||||||
|
"messageformat": "Lower your hand?",
|
||||||
|
"description": "Shown in a call when the user has their hand raised but has been talking, next to a button to lower their hand."
|
||||||
|
},
|
||||||
|
"icu:CallControls__LowerHandSuggestionToast--button": {
|
||||||
|
"messageformat": "Lower",
|
||||||
|
"description": "Text of button that, when clicked, will lower your raised hand after you've been speaking"
|
||||||
|
},
|
||||||
"icu:CallControls__RingingToast--ringing-on": {
|
"icu:CallControls__RingingToast--ringing-on": {
|
||||||
"messageformat": "Ringing on",
|
"messageformat": "Ringing on",
|
||||||
"description": "Shown in a group call lobby when call ringing is disabled, then the user enables ringing using the Ringing toggle button."
|
"description": "Shown in a group call lobby when call ringing is disabled, then the user enables ringing using the Ringing toggle button."
|
||||||
|
|
|
@ -181,6 +181,7 @@ const getActiveCallForCallLink = (
|
||||||
pendingParticipants: overrideProps.pendingParticipants ?? [],
|
pendingParticipants: overrideProps.pendingParticipants ?? [],
|
||||||
raisedHands: new Set<number>(),
|
raisedHands: new Set<number>(),
|
||||||
remoteAudioLevels: new Map<number, number>(),
|
remoteAudioLevels: new Map<number, number>(),
|
||||||
|
suggestLowerHand: false,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -232,6 +233,7 @@ export function OngoingGroupCall(): JSX.Element {
|
||||||
raisedHands: new Set<number>(),
|
raisedHands: new Set<number>(),
|
||||||
remoteParticipants: [],
|
remoteParticipants: [],
|
||||||
remoteAudioLevels: new Map<number, number>(),
|
remoteAudioLevels: new Map<number, number>(),
|
||||||
|
suggestLowerHand: false,
|
||||||
},
|
},
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -74,6 +74,7 @@ type GroupCallOverrideProps = OverridePropsBase & {
|
||||||
raisedHands?: Set<number>;
|
raisedHands?: Set<number>;
|
||||||
remoteParticipants?: Array<GroupCallRemoteParticipantType>;
|
remoteParticipants?: Array<GroupCallRemoteParticipantType>;
|
||||||
remoteAudioLevel?: number;
|
remoteAudioLevel?: number;
|
||||||
|
suggestLowerHand?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const createActiveDirectCallProp = (
|
const createActiveDirectCallProp = (
|
||||||
|
@ -153,6 +154,7 @@ const createActiveGroupCallProp = (overrideProps: GroupCallOverrideProps) => ({
|
||||||
])
|
])
|
||||||
),
|
),
|
||||||
reactions: overrideProps.reactions || [],
|
reactions: overrideProps.reactions || [],
|
||||||
|
suggestLowerHand: overrideProps.suggestLowerHand ?? false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const createActiveCallProp = (
|
const createActiveCallProp = (
|
||||||
|
@ -799,6 +801,37 @@ export function GroupCallHandRaising(): JSX.Element {
|
||||||
return <CallScreen {...props} activeCall={activeCall} />;
|
return <CallScreen {...props} activeCall={activeCall} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function GroupCallSuggestLowerHand(): JSX.Element {
|
||||||
|
const remoteParticipants = allRemoteParticipants.slice(0, 10);
|
||||||
|
|
||||||
|
const [props, setProps] = React.useState(
|
||||||
|
createProps({
|
||||||
|
callMode: CallMode.Group,
|
||||||
|
remoteParticipants,
|
||||||
|
raisedHands: new Set([LOCAL_DEMUX_ID]),
|
||||||
|
viewMode: CallViewMode.Sidebar,
|
||||||
|
suggestLowerHand: false,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
setTimeout(
|
||||||
|
() =>
|
||||||
|
setProps(
|
||||||
|
createProps({
|
||||||
|
callMode: CallMode.Group,
|
||||||
|
remoteParticipants,
|
||||||
|
viewMode: CallViewMode.Sidebar,
|
||||||
|
suggestLowerHand: true,
|
||||||
|
})
|
||||||
|
),
|
||||||
|
200
|
||||||
|
);
|
||||||
|
}, [remoteParticipants]);
|
||||||
|
|
||||||
|
return <CallScreen {...props} />;
|
||||||
|
}
|
||||||
|
|
||||||
// Every [frequency] ms, all hands are lowered and [random min to max] random hands
|
// Every [frequency] ms, all hands are lowered and [random min to max] random hands
|
||||||
// are raised
|
// are raised
|
||||||
function useHandRaiser(
|
function useHandRaiser(
|
||||||
|
|
|
@ -855,6 +855,13 @@ export function CallScreen({
|
||||||
outgoingRing={undefined}
|
outgoingRing={undefined}
|
||||||
raisedHands={raisedHands}
|
raisedHands={raisedHands}
|
||||||
renderRaisedHandsToast={renderRaisedHandsToast}
|
renderRaisedHandsToast={renderRaisedHandsToast}
|
||||||
|
handleLowerHand={() => toggleRaiseHand(false)}
|
||||||
|
suggestLowerHand={
|
||||||
|
isGroupOrAdhocActiveCall(activeCall)
|
||||||
|
? activeCall.suggestLowerHand
|
||||||
|
: false
|
||||||
|
}
|
||||||
|
isHandRaised={localHandRaised}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
/>
|
/>
|
||||||
{isCallLinkAdmin ? (
|
{isCallLinkAdmin ? (
|
||||||
|
|
|
@ -144,6 +144,7 @@ export function GroupCall(args: PropsType): JSX.Element {
|
||||||
raisedHands: new Set<number>(),
|
raisedHands: new Set<number>(),
|
||||||
remoteParticipants: [],
|
remoteParticipants: [],
|
||||||
remoteAudioLevels: new Map<number, number>(),
|
remoteAudioLevels: new Map<number, number>(),
|
||||||
|
suggestLowerHand: false,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,6 +11,7 @@ import { usePrevious } from '../hooks/usePrevious';
|
||||||
import { difference as setDifference } from '../util/setUtil';
|
import { difference as setDifference } from '../util/setUtil';
|
||||||
import { isMoreRecentThan } from '../util/timestamp';
|
import { isMoreRecentThan } from '../util/timestamp';
|
||||||
import { isGroupOrAdhocActiveCall } from '../util/isGroupOrAdhocCall';
|
import { isGroupOrAdhocActiveCall } from '../util/isGroupOrAdhocCall';
|
||||||
|
import { SECOND } from '../util/durations';
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
activeCall: ActiveCallType;
|
activeCall: ActiveCallType;
|
||||||
|
@ -251,6 +252,75 @@ function useRaisedHandsToast({
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function useLowerHandSuggestionToast({
|
||||||
|
suggestLowerHand,
|
||||||
|
handleLowerHand,
|
||||||
|
i18n,
|
||||||
|
isHandRaised,
|
||||||
|
}: {
|
||||||
|
suggestLowerHand: boolean | undefined;
|
||||||
|
i18n: LocalizerType;
|
||||||
|
handleLowerHand: (() => void) | undefined;
|
||||||
|
isHandRaised: boolean | undefined;
|
||||||
|
}): void {
|
||||||
|
const previousSuggestLowerHand = usePrevious(
|
||||||
|
suggestLowerHand,
|
||||||
|
suggestLowerHand
|
||||||
|
);
|
||||||
|
const { showToast, hideToast } = useCallingToasts();
|
||||||
|
const SUGGEST_LOWER_HAND_TOAST_KEY = 'SUGGEST_LOWER_HAND_TOAST_KEY';
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!handleLowerHand) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
previousSuggestLowerHand !== undefined &&
|
||||||
|
suggestLowerHand !== previousSuggestLowerHand
|
||||||
|
) {
|
||||||
|
if (suggestLowerHand && isHandRaised) {
|
||||||
|
showToast({
|
||||||
|
key: SUGGEST_LOWER_HAND_TOAST_KEY,
|
||||||
|
content: (
|
||||||
|
<div className="CallingRaisedHandsToast__Content">
|
||||||
|
<span className="CallingRaisedHandsToast__HandIcon" />
|
||||||
|
{i18n('icu:CallControls__LowerHandSuggestionToast')}
|
||||||
|
<button
|
||||||
|
className="CallingRaisedHandsToasts__Link"
|
||||||
|
type="button"
|
||||||
|
onClick={() => {
|
||||||
|
handleLowerHand();
|
||||||
|
hideToast(SUGGEST_LOWER_HAND_TOAST_KEY);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{i18n('icu:CallControls__LowerHandSuggestionToast--button')}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
dismissable: false,
|
||||||
|
autoClose: true,
|
||||||
|
lifetime: 10 * SECOND,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [
|
||||||
|
suggestLowerHand,
|
||||||
|
handleLowerHand,
|
||||||
|
previousSuggestLowerHand,
|
||||||
|
hideToast,
|
||||||
|
showToast,
|
||||||
|
SUGGEST_LOWER_HAND_TOAST_KEY,
|
||||||
|
isHandRaised,
|
||||||
|
i18n,
|
||||||
|
]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isHandRaised) {
|
||||||
|
hideToast(SUGGEST_LOWER_HAND_TOAST_KEY);
|
||||||
|
}
|
||||||
|
}, [isHandRaised, hideToast]);
|
||||||
|
}
|
||||||
|
|
||||||
type CallingButtonToastsType = {
|
type CallingButtonToastsType = {
|
||||||
hasLocalAudio: boolean;
|
hasLocalAudio: boolean;
|
||||||
outgoingRing: boolean | undefined;
|
outgoingRing: boolean | undefined;
|
||||||
|
@ -258,6 +328,9 @@ type CallingButtonToastsType = {
|
||||||
renderRaisedHandsToast?: (
|
renderRaisedHandsToast?: (
|
||||||
hands: Array<number>
|
hands: Array<number>
|
||||||
) => JSX.Element | string | undefined;
|
) => JSX.Element | string | undefined;
|
||||||
|
suggestLowerHand?: boolean;
|
||||||
|
isHandRaised?: boolean;
|
||||||
|
handleLowerHand?: () => void;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -284,11 +357,20 @@ function CallingButtonToasts({
|
||||||
outgoingRing,
|
outgoingRing,
|
||||||
raisedHands,
|
raisedHands,
|
||||||
renderRaisedHandsToast,
|
renderRaisedHandsToast,
|
||||||
|
suggestLowerHand,
|
||||||
|
handleLowerHand,
|
||||||
|
isHandRaised,
|
||||||
i18n,
|
i18n,
|
||||||
}: CallingButtonToastsType) {
|
}: CallingButtonToastsType) {
|
||||||
useMutedToast({ hasLocalAudio, i18n });
|
useMutedToast({ hasLocalAudio, i18n });
|
||||||
useOutgoingRingToast({ outgoingRing, i18n });
|
useOutgoingRingToast({ outgoingRing, i18n });
|
||||||
useRaisedHandsToast({ raisedHands, renderRaisedHandsToast });
|
useRaisedHandsToast({ raisedHands, renderRaisedHandsToast });
|
||||||
|
useLowerHandSuggestionToast({
|
||||||
|
suggestLowerHand,
|
||||||
|
i18n,
|
||||||
|
handleLowerHand,
|
||||||
|
isHandRaised,
|
||||||
|
});
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ import type {
|
||||||
CallId,
|
CallId,
|
||||||
DeviceId,
|
DeviceId,
|
||||||
GroupCallObserver,
|
GroupCallObserver,
|
||||||
SpeechEvent,
|
|
||||||
PeekInfo,
|
PeekInfo,
|
||||||
UserId,
|
UserId,
|
||||||
VideoFrameSource,
|
VideoFrameSource,
|
||||||
|
@ -39,6 +38,7 @@ import {
|
||||||
RingRTC,
|
RingRTC,
|
||||||
RingUpdate,
|
RingUpdate,
|
||||||
GroupCallKind,
|
GroupCallKind,
|
||||||
|
SpeechEvent,
|
||||||
} from '@signalapp/ringrtc';
|
} from '@signalapp/ringrtc';
|
||||||
import { uniqBy, noop, compact } from 'lodash';
|
import { uniqBy, noop, compact } from 'lodash';
|
||||||
|
|
||||||
|
@ -158,6 +158,7 @@ import { createIdenticon } from '../util/createIdenticon';
|
||||||
import { getColorForCallLink } from '../util/getColorForCallLink';
|
import { getColorForCallLink } from '../util/getColorForCallLink';
|
||||||
import { getUseRingrtcAdm } from '../util/ringrtc/ringrtcAdm';
|
import { getUseRingrtcAdm } from '../util/ringrtc/ringrtcAdm';
|
||||||
import OS from '../util/os/osMain';
|
import OS from '../util/os/osMain';
|
||||||
|
import { isLowerHandSuggestionEnabled } from '../util/isLowerHandSuggestionEnabled';
|
||||||
|
|
||||||
const { wasGroupCallRingPreviouslyCanceled } = DataReader;
|
const { wasGroupCallRingPreviouslyCanceled } = DataReader;
|
||||||
const {
|
const {
|
||||||
|
@ -207,10 +208,12 @@ type CallingReduxInterface = Pick<
|
||||||
| 'refreshIODevices'
|
| 'refreshIODevices'
|
||||||
| 'remoteSharingScreenChange'
|
| 'remoteSharingScreenChange'
|
||||||
| 'remoteVideoChange'
|
| 'remoteVideoChange'
|
||||||
|
| 'sendGroupCallRaiseHand'
|
||||||
| 'startCallingLobby'
|
| 'startCallingLobby'
|
||||||
| 'startCallLinkLobby'
|
| 'startCallLinkLobby'
|
||||||
| 'startCallLinkLobbyByRoomId'
|
| 'startCallLinkLobbyByRoomId'
|
||||||
| 'peekNotConnectedGroupCall'
|
| 'peekNotConnectedGroupCall'
|
||||||
|
| 'setSuggestLowerHand'
|
||||||
> & {
|
> & {
|
||||||
areAnyCallsActiveOrRinging(): boolean;
|
areAnyCallsActiveOrRinging(): boolean;
|
||||||
};
|
};
|
||||||
|
@ -1479,8 +1482,22 @@ export class CallingClass {
|
||||||
endedReason,
|
endedReason,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
onSpeechEvent: (_groupCall: GroupCall, _event: SpeechEvent) => {
|
onSpeechEvent: (_groupCall: GroupCall, event: SpeechEvent) => {
|
||||||
// Implementation to come later
|
if (!isLowerHandSuggestionEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log.info('GroupCall#onSpeechEvent', event);
|
||||||
|
if (event === SpeechEvent.LowerHandSuggestion) {
|
||||||
|
this.reduxInterface?.setSuggestLowerHand(true);
|
||||||
|
} else if (event === SpeechEvent.StoppedSpeaking) {
|
||||||
|
this.reduxInterface?.setSuggestLowerHand(false);
|
||||||
|
} else {
|
||||||
|
log.error(
|
||||||
|
'GroupCall#onSpeechEvent, unknown speechEvent',
|
||||||
|
SpeechEvent,
|
||||||
|
Errors.toLogFormat(missingCaseError(event))
|
||||||
|
);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -193,6 +193,7 @@ export type ActiveCallStateType = {
|
||||||
settingsDialogOpen: boolean;
|
settingsDialogOpen: boolean;
|
||||||
showNeedsScreenRecordingPermissionsWarning?: boolean;
|
showNeedsScreenRecordingPermissionsWarning?: boolean;
|
||||||
showParticipantsList: boolean;
|
showParticipantsList: boolean;
|
||||||
|
suggestLowerHand?: boolean;
|
||||||
reactions?: ActiveCallReactionsType;
|
reactions?: ActiveCallReactionsType;
|
||||||
};
|
};
|
||||||
export type WaitingCallStateType = ReadonlyDeep<{
|
export type WaitingCallStateType = ReadonlyDeep<{
|
||||||
|
@ -650,6 +651,7 @@ const SET_OUTGOING_RING = 'calling/SET_OUTGOING_RING';
|
||||||
const SET_PRESENTING = 'calling/SET_PRESENTING';
|
const SET_PRESENTING = 'calling/SET_PRESENTING';
|
||||||
const SET_PRESENTING_SOURCES = 'calling/SET_PRESENTING_SOURCES';
|
const SET_PRESENTING_SOURCES = 'calling/SET_PRESENTING_SOURCES';
|
||||||
const SET_CAPTURER_BATON = 'calling/SET_CAPTURER_BATON';
|
const SET_CAPTURER_BATON = 'calling/SET_CAPTURER_BATON';
|
||||||
|
const SUGGEST_LOWER_HAND = 'calling/SUGGEST_LOWER_HAND';
|
||||||
const TOGGLE_NEEDS_SCREEN_RECORDING_PERMISSIONS =
|
const TOGGLE_NEEDS_SCREEN_RECORDING_PERMISSIONS =
|
||||||
'calling/TOGGLE_NEEDS_SCREEN_RECORDING_PERMISSIONS';
|
'calling/TOGGLE_NEEDS_SCREEN_RECORDING_PERMISSIONS';
|
||||||
const START_DIRECT_CALL = 'calling/START_DIRECT_CALL';
|
const START_DIRECT_CALL = 'calling/START_DIRECT_CALL';
|
||||||
|
@ -915,6 +917,10 @@ type StartDirectCallActionType = ReadonlyDeep<{
|
||||||
type: 'calling/START_DIRECT_CALL';
|
type: 'calling/START_DIRECT_CALL';
|
||||||
payload: StartDirectCallType;
|
payload: StartDirectCallType;
|
||||||
}>;
|
}>;
|
||||||
|
type SuggestLowerHandActionType = ReadonlyDeep<{
|
||||||
|
type: 'calling/SUGGEST_LOWER_HAND';
|
||||||
|
payload: { suggestLowerHand: boolean };
|
||||||
|
}>;
|
||||||
|
|
||||||
type ToggleNeedsScreenRecordingPermissionsActionType = ReadonlyDeep<{
|
type ToggleNeedsScreenRecordingPermissionsActionType = ReadonlyDeep<{
|
||||||
type: 'calling/TOGGLE_NEEDS_SCREEN_RECORDING_PERMISSIONS';
|
type: 'calling/TOGGLE_NEEDS_SCREEN_RECORDING_PERMISSIONS';
|
||||||
|
@ -993,6 +999,7 @@ export type CallingActionType =
|
||||||
| TogglePipActionType
|
| TogglePipActionType
|
||||||
| SetPresentingFulfilledActionType
|
| SetPresentingFulfilledActionType
|
||||||
| ToggleSettingsActionType
|
| ToggleSettingsActionType
|
||||||
|
| SuggestLowerHandActionType
|
||||||
| SwitchToPresentationViewActionType
|
| SwitchToPresentationViewActionType
|
||||||
| SwitchFromPresentationViewActionType
|
| SwitchFromPresentationViewActionType
|
||||||
| WaitingForCallingLobbyActionType
|
| WaitingForCallingLobbyActionType
|
||||||
|
@ -1622,6 +1629,15 @@ function hangUpActiveCall(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setSuggestLowerHand(
|
||||||
|
suggestLowerHand: boolean
|
||||||
|
): SuggestLowerHandActionType {
|
||||||
|
return {
|
||||||
|
type: SUGGEST_LOWER_HAND,
|
||||||
|
payload: { suggestLowerHand },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function sendGroupCallRaiseHand(
|
function sendGroupCallRaiseHand(
|
||||||
payload: SendGroupCallRaiseHandType
|
payload: SendGroupCallRaiseHandType
|
||||||
): ThunkAction<void, RootStateType, unknown, SendGroupCallRaiseHandActionType> {
|
): ThunkAction<void, RootStateType, unknown, SendGroupCallRaiseHandActionType> {
|
||||||
|
@ -2696,6 +2712,7 @@ export const actions = {
|
||||||
setLocalVideo,
|
setLocalVideo,
|
||||||
setOutgoingRing,
|
setOutgoingRing,
|
||||||
setRendererCanvas,
|
setRendererCanvas,
|
||||||
|
setSuggestLowerHand,
|
||||||
startCall,
|
startCall,
|
||||||
startCallLinkLobby,
|
startCallLinkLobby,
|
||||||
startCallLinkLobbyByRoomId,
|
startCallLinkLobbyByRoomId,
|
||||||
|
@ -4035,5 +4052,23 @@ export function reducer(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action.type === SUGGEST_LOWER_HAND) {
|
||||||
|
const { suggestLowerHand } = action.payload;
|
||||||
|
const { activeCallState } = state;
|
||||||
|
|
||||||
|
if (activeCallState?.state !== 'Active') {
|
||||||
|
log.warn('Cannot suggest lower hand when there is no active call');
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
activeCallState: {
|
||||||
|
...activeCallState,
|
||||||
|
suggestLowerHand,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -333,6 +333,7 @@ const mapStateToActiveCallProp = (
|
||||||
raisedHands,
|
raisedHands,
|
||||||
remoteParticipants,
|
remoteParticipants,
|
||||||
remoteAudioLevels: call.remoteAudioLevels || new Map<number, number>(),
|
remoteAudioLevels: call.remoteAudioLevels || new Map<number, number>(),
|
||||||
|
suggestLowerHand: Boolean(activeCallState.suggestLowerHand),
|
||||||
} satisfies ActiveGroupCallType;
|
} satisfies ActiveGroupCallType;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -95,6 +95,7 @@ export type ActiveGroupCallType = ActiveCallBaseType & {
|
||||||
raisedHands: Set<number>;
|
raisedHands: Set<number>;
|
||||||
remoteParticipants: Array<GroupCallRemoteParticipantType>;
|
remoteParticipants: Array<GroupCallRemoteParticipantType>;
|
||||||
remoteAudioLevels: Map<number, number>;
|
remoteAudioLevels: Map<number, number>;
|
||||||
|
suggestLowerHand: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type ActiveCallType = ActiveDirectCallType | ActiveGroupCallType;
|
export type ActiveCallType = ActiveDirectCallType | ActiveGroupCallType;
|
||||||
|
|
16
ts/util/isLowerHandSuggestionEnabled.ts
Normal file
16
ts/util/isLowerHandSuggestionEnabled.ts
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
// Copyright 2024 Signal Messenger, LLC
|
||||||
|
// SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
|
||||||
|
import * as RemoteConfig from '../RemoteConfig';
|
||||||
|
import { isProduction } from './version';
|
||||||
|
|
||||||
|
export function isLowerHandSuggestionEnabled(): boolean {
|
||||||
|
if (
|
||||||
|
isProduction(window.getVersion()) ||
|
||||||
|
!RemoteConfig.isEnabled('desktop.internalUser')
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue