ConversationView: Move call/mute functions into redux
This commit is contained in:
parent
8fe51cc854
commit
92a512a16d
17 changed files with 353 additions and 287 deletions
|
@ -1,20 +0,0 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { Toast } from './Toast';
|
||||
|
||||
type PropsType = {
|
||||
i18n: LocalizerType;
|
||||
onClose: () => unknown;
|
||||
};
|
||||
|
||||
export function ToastCannotStartGroupCall({
|
||||
i18n,
|
||||
onClose,
|
||||
}: PropsType): JSX.Element {
|
||||
return (
|
||||
<Toast onClose={onClose}>{i18n('GroupV2--cannot-start-group-call')}</Toast>
|
||||
);
|
||||
}
|
|
@ -30,72 +30,6 @@ export function ToastManager({
|
|||
}
|
||||
|
||||
const { toastType } = toast;
|
||||
if (toastType === ToastType.Error) {
|
||||
return (
|
||||
<Toast
|
||||
autoDismissDisabled
|
||||
onClose={hideToast}
|
||||
toastAction={{
|
||||
label: i18n('Toast--error--action'),
|
||||
onClick: () => window.showDebugLog(),
|
||||
}}
|
||||
>
|
||||
{i18n('Toast--error')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.MessageBodyTooLong) {
|
||||
return <ToastMessageBodyTooLong i18n={i18n} onClose={hideToast} />;
|
||||
}
|
||||
|
||||
if (toastType === ToastType.StoryReact) {
|
||||
return (
|
||||
<Toast onClose={hideToast} timeout={SHORT_TIMEOUT}>
|
||||
{i18n('Stories__toast--sending-reaction')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.StoryReply) {
|
||||
return (
|
||||
<Toast onClose={hideToast} timeout={SHORT_TIMEOUT}>
|
||||
{i18n('Stories__toast--sending-reply')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.StoryMuted) {
|
||||
return (
|
||||
<Toast onClose={hideToast} timeout={SHORT_TIMEOUT}>
|
||||
{i18n('Stories__toast--hasNoSound')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.StoryVideoTooLong) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n('StoryCreator__error--video-too-long')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.StoryVideoUnsupported) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n('StoryCreator__error--video-unsupported')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.StoryVideoError) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n('StoryCreator__error--video-error')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.AddingUserToGroup) {
|
||||
return (
|
||||
|
@ -108,21 +42,10 @@ export function ToastManager({
|
|||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.UserAddedToGroup) {
|
||||
if (toastType === ToastType.CannotStartGroupCall) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n(
|
||||
'AddUserToAnotherGroupModal__toast--user-added-to-group',
|
||||
toast.parameters
|
||||
)}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.FailedToDeleteUsername) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n('ProfileEditor--username--delete-general-error')}
|
||||
{i18n('GroupV2--cannot-start-group-call', toast.parameters)}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
@ -147,5 +70,91 @@ export function ToastManager({
|
|||
return <Toast onClose={hideToast}>{i18n('deleteForEveryoneFailed')}</Toast>;
|
||||
}
|
||||
|
||||
if (toastType === ToastType.Error) {
|
||||
return (
|
||||
<Toast
|
||||
autoDismissDisabled
|
||||
onClose={hideToast}
|
||||
toastAction={{
|
||||
label: i18n('Toast--error--action'),
|
||||
onClick: () => window.showDebugLog(),
|
||||
}}
|
||||
>
|
||||
{i18n('Toast--error')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.FailedToDeleteUsername) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n('ProfileEditor--username--delete-general-error')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.MessageBodyTooLong) {
|
||||
return <ToastMessageBodyTooLong i18n={i18n} onClose={hideToast} />;
|
||||
}
|
||||
|
||||
if (toastType === ToastType.StoryMuted) {
|
||||
return (
|
||||
<Toast onClose={hideToast} timeout={SHORT_TIMEOUT}>
|
||||
{i18n('Stories__toast--hasNoSound')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.StoryReact) {
|
||||
return (
|
||||
<Toast onClose={hideToast} timeout={SHORT_TIMEOUT}>
|
||||
{i18n('Stories__toast--sending-reaction')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.StoryReply) {
|
||||
return (
|
||||
<Toast onClose={hideToast} timeout={SHORT_TIMEOUT}>
|
||||
{i18n('Stories__toast--sending-reply')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.StoryVideoError) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n('StoryCreator__error--video-error')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.StoryVideoTooLong) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n('StoryCreator__error--video-too-long')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.StoryVideoUnsupported) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n('StoryCreator__error--video-unsupported')}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
if (toastType === ToastType.UserAddedToGroup) {
|
||||
return (
|
||||
<Toast onClose={hideToast}>
|
||||
{i18n(
|
||||
'AddUserToAnotherGroupModal__toast--user-added-to-group',
|
||||
toast.parameters
|
||||
)}
|
||||
</Toast>
|
||||
);
|
||||
}
|
||||
|
||||
throw missingCaseError(toastType);
|
||||
}
|
||||
|
|
|
@ -41,7 +41,6 @@ const commonProps = {
|
|||
setDisappearingMessages: action('setDisappearingMessages'),
|
||||
destroyMessages: action('destroyMessages'),
|
||||
onSearchInConversation: action('onSearchInConversation'),
|
||||
onSetMuteNotifications: action('onSetMuteNotifications'),
|
||||
onOutgoingAudioCallInConversation: action(
|
||||
'onOutgoingAudioCallInConversation'
|
||||
),
|
||||
|
@ -57,6 +56,7 @@ const commonProps = {
|
|||
onMarkUnread: action('onMarkUnread'),
|
||||
onMoveToInbox: action('onMoveToInbox'),
|
||||
onSetPin: action('onSetPin'),
|
||||
setMuteExpiration: action('onSetMuteNotifications'),
|
||||
viewUserStories: action('viewUserStories'),
|
||||
};
|
||||
|
||||
|
|
|
@ -80,11 +80,10 @@ export type PropsDataType = {
|
|||
>;
|
||||
|
||||
export type PropsActionsType = {
|
||||
onSetMuteNotifications: (seconds: number) => void;
|
||||
destroyMessages: (conversationId: string) => void;
|
||||
onSearchInConversation: () => void;
|
||||
onOutgoingAudioCallInConversation: () => void;
|
||||
onOutgoingVideoCallInConversation: () => void;
|
||||
onOutgoingAudioCallInConversation: (conversationId: string) => void;
|
||||
onOutgoingVideoCallInConversation: (conversationId: string) => void;
|
||||
onSetPin: (value: boolean) => void;
|
||||
|
||||
onShowConversationDetails: () => void;
|
||||
|
@ -95,6 +94,7 @@ export type PropsActionsType = {
|
|||
onArchive: () => void;
|
||||
onMarkUnread: () => void;
|
||||
onMoveToInbox: () => void;
|
||||
setMuteExpiration: (conversationId: string, seconds: number) => void;
|
||||
setDisappearingMessages: (
|
||||
conversationId: string,
|
||||
seconds: DurationInSeconds
|
||||
|
@ -349,12 +349,12 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
|
|||
onArchive,
|
||||
onMarkUnread,
|
||||
onMoveToInbox,
|
||||
onSetMuteNotifications,
|
||||
onSetPin,
|
||||
onShowAllMedia,
|
||||
onShowConversationDetails,
|
||||
onShowGroupMembers,
|
||||
setDisappearingMessages,
|
||||
setMuteExpiration,
|
||||
type,
|
||||
} = this.props;
|
||||
|
||||
|
@ -371,7 +371,7 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
|
|||
{isMuted ? (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
onSetMuteNotifications(0);
|
||||
setMuteExpiration(id, 0);
|
||||
}}
|
||||
>
|
||||
{i18n('unmute')}
|
||||
|
@ -379,7 +379,7 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
|
|||
) : (
|
||||
<MenuItem
|
||||
onClick={() => {
|
||||
onSetMuteNotifications(Number.MAX_SAFE_INTEGER);
|
||||
setMuteExpiration(id, Number.MAX_SAFE_INTEGER);
|
||||
}}
|
||||
>
|
||||
{i18n('muteAlways')}
|
||||
|
@ -465,7 +465,7 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
|
|||
key={item.name}
|
||||
disabled={item.disabled}
|
||||
onClick={() => {
|
||||
onSetMuteNotifications(item.value);
|
||||
setMuteExpiration(id, item.value);
|
||||
}}
|
||||
>
|
||||
{item.name}
|
||||
|
@ -676,6 +676,7 @@ export class ConversationHeader extends React.Component<PropsType, StateType> {
|
|||
announcementsOnly={announcementsOnly}
|
||||
areWeAdmin={areWeAdmin}
|
||||
i18n={i18n}
|
||||
id={id}
|
||||
isNarrow={isNarrow}
|
||||
onOutgoingAudioCallInConversation={
|
||||
onOutgoingAudioCallInConversation
|
||||
|
@ -702,6 +703,7 @@ function OutgoingCallButtons({
|
|||
announcementsOnly,
|
||||
areWeAdmin,
|
||||
i18n,
|
||||
id,
|
||||
isNarrow,
|
||||
onOutgoingAudioCallInConversation,
|
||||
onOutgoingVideoCallInConversation,
|
||||
|
@ -712,6 +714,7 @@ function OutgoingCallButtons({
|
|||
| 'announcementsOnly'
|
||||
| 'areWeAdmin'
|
||||
| 'i18n'
|
||||
| 'id'
|
||||
| 'onOutgoingAudioCallInConversation'
|
||||
| 'onOutgoingVideoCallInConversation'
|
||||
| 'outgoingCallButtonStyle'
|
||||
|
@ -729,14 +732,14 @@ function OutgoingCallButtons({
|
|||
: undefined
|
||||
)}
|
||||
disabled={showBackButton}
|
||||
onClick={onOutgoingVideoCallInConversation}
|
||||
onClick={() => onOutgoingVideoCallInConversation(id)}
|
||||
type="button"
|
||||
/>
|
||||
);
|
||||
|
||||
const startCallShortcuts = useStartCallShortcuts(
|
||||
onOutgoingAudioCallInConversation,
|
||||
onOutgoingVideoCallInConversation
|
||||
() => onOutgoingAudioCallInConversation(id),
|
||||
() => onOutgoingVideoCallInConversation(id)
|
||||
);
|
||||
useKeyboardShortcuts(startCallShortcuts);
|
||||
|
||||
|
@ -751,7 +754,7 @@ function OutgoingCallButtons({
|
|||
{videoButton}
|
||||
<button
|
||||
type="button"
|
||||
onClick={onOutgoingAudioCallInConversation}
|
||||
onClick={() => onOutgoingAudioCallInConversation(id)}
|
||||
className={classNames(
|
||||
'module-ConversationHeader__button',
|
||||
'module-ConversationHeader__button--audio',
|
||||
|
@ -772,7 +775,7 @@ function OutgoingCallButtons({
|
|||
showBackButton ? null : 'module-ConversationHeader__button--show'
|
||||
)}
|
||||
disabled={showBackButton}
|
||||
onClick={onOutgoingVideoCallInConversation}
|
||||
onClick={() => onOutgoingVideoCallInConversation(id)}
|
||||
type="button"
|
||||
>
|
||||
{isNarrow ? null : i18n('joinOngoingCall')}
|
||||
|
|
|
@ -101,9 +101,6 @@ export type StateProps = {
|
|||
onUnblock: () => void;
|
||||
theme: ThemeType;
|
||||
userAvatarData: Array<AvatarDataType>;
|
||||
setMuteExpiration: (muteExpiresAt: undefined | number) => unknown;
|
||||
onOutgoingAudioCallInConversation: () => unknown;
|
||||
onOutgoingVideoCallInConversation: () => unknown;
|
||||
renderChooseGroupMembersModal: (
|
||||
props: SmartChooseGroupMembersModalPropsType
|
||||
) => JSX.Element;
|
||||
|
@ -115,10 +112,13 @@ export type StateProps = {
|
|||
type ActionProps = {
|
||||
deleteAvatarFromDisk: DeleteAvatarFromDiskActionType;
|
||||
loadRecentMediaItems: (id: string, limit: number) => void;
|
||||
onOutgoingAudioCallInConversation: (conversationId: string) => unknown;
|
||||
onOutgoingVideoCallInConversation: (conversationId: string) => unknown;
|
||||
replaceAvatar: ReplaceAvatarActionType;
|
||||
saveAvatarToDisk: SaveAvatarToDiskActionType;
|
||||
searchInConversation: (id: string) => unknown;
|
||||
setDisappearingMessages: (id: string, seconds: DurationInSeconds) => void;
|
||||
setMuteExpiration: (id: string, muteExpiresAt: undefined | number) => unknown;
|
||||
showContactModal: (contactId: string, conversationId?: string) => void;
|
||||
showConversation: ShowConversationType;
|
||||
toggleAddUserToAnotherGroupModal: (contactId?: string) => void;
|
||||
|
@ -291,6 +291,7 @@ export function ConversationDetails({
|
|||
modalNode = (
|
||||
<ConversationNotificationsModal
|
||||
i18n={i18n}
|
||||
id={conversation.id}
|
||||
muteExpiresAt={conversation.muteExpiresAt}
|
||||
onClose={() => {
|
||||
setModalState(ModalState.NothingOpen);
|
||||
|
@ -305,7 +306,7 @@ export function ConversationDetails({
|
|||
dialogName="ConversationDetails.unmuteNotifications"
|
||||
actions={[
|
||||
{
|
||||
action: () => setMuteExpiration(0),
|
||||
action: () => setMuteExpiration(conversation.id, 0),
|
||||
style: 'affirmative',
|
||||
text: i18n('unmute'),
|
||||
},
|
||||
|
@ -354,14 +355,16 @@ export function ConversationDetails({
|
|||
<ConversationDetailsCallButton
|
||||
disabled={hasActiveCall}
|
||||
i18n={i18n}
|
||||
onClick={onOutgoingVideoCallInConversation}
|
||||
onClick={() => onOutgoingVideoCallInConversation(conversation.id)}
|
||||
type="video"
|
||||
/>
|
||||
{!isGroup && (
|
||||
<ConversationDetailsCallButton
|
||||
disabled={hasActiveCall}
|
||||
i18n={i18n}
|
||||
onClick={onOutgoingAudioCallInConversation}
|
||||
onClick={() =>
|
||||
onOutgoingAudioCallInConversation(conversation.id)
|
||||
}
|
||||
type="audio"
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -12,13 +12,18 @@ import { Button, ButtonVariant } from '../../Button';
|
|||
|
||||
type PropsType = {
|
||||
i18n: LocalizerType;
|
||||
id: string;
|
||||
muteExpiresAt: undefined | number;
|
||||
onClose: () => unknown;
|
||||
setMuteExpiration: (muteExpiresAt: undefined | number) => unknown;
|
||||
setMuteExpiration: (
|
||||
conversationId: string,
|
||||
muteExpiresAt: undefined | number
|
||||
) => unknown;
|
||||
};
|
||||
|
||||
export function ConversationNotificationsModal({
|
||||
i18n,
|
||||
id,
|
||||
muteExpiresAt,
|
||||
onClose,
|
||||
setMuteExpiration,
|
||||
|
@ -40,7 +45,7 @@ export function ConversationNotificationsModal({
|
|||
muteExpirationValue,
|
||||
'NotificationSettings: mute ms was not an integer'
|
||||
);
|
||||
setMuteExpiration(ms);
|
||||
setMuteExpiration(id, ms);
|
||||
onClose();
|
||||
};
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ export default {
|
|||
};
|
||||
|
||||
const getCommonProps = () => ({
|
||||
id: 'conversation-id',
|
||||
muteExpiresAt: undefined,
|
||||
conversationType: 'group' as const,
|
||||
dontNotifyForMentionsIfMuted: false,
|
||||
|
|
|
@ -15,17 +15,23 @@ import { parseIntOrThrow } from '../../../util/parseIntOrThrow';
|
|||
import { useUniqueId } from '../../../hooks/useUniqueId';
|
||||
|
||||
type PropsType = {
|
||||
id: string;
|
||||
conversationType: ConversationTypeType;
|
||||
dontNotifyForMentionsIfMuted: boolean;
|
||||
i18n: LocalizerType;
|
||||
muteExpiresAt: undefined | number;
|
||||
setDontNotifyForMentionsIfMuted: (
|
||||
conversationId: string,
|
||||
dontNotifyForMentionsIfMuted: boolean
|
||||
) => unknown;
|
||||
setMuteExpiration: (muteExpiresAt: undefined | number) => unknown;
|
||||
setMuteExpiration: (
|
||||
conversationId: string,
|
||||
muteExpiresAt: undefined | number
|
||||
) => unknown;
|
||||
};
|
||||
|
||||
export function ConversationNotificationsSettings({
|
||||
id,
|
||||
conversationType,
|
||||
dontNotifyForMentionsIfMuted,
|
||||
i18n,
|
||||
|
@ -62,11 +68,11 @@ export function ConversationNotificationsSettings({
|
|||
rawValue,
|
||||
'NotificationSettings: mute ms was not an integer'
|
||||
);
|
||||
setMuteExpiration(ms);
|
||||
setMuteExpiration(id, ms);
|
||||
};
|
||||
|
||||
const onChangeDontNotifyForMentionsIfMuted = (rawValue: string) => {
|
||||
setDontNotifyForMentionsIfMuted(rawValue === 'yes');
|
||||
setDontNotifyForMentionsIfMuted(id, rawValue === 'yes');
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
|
@ -47,6 +47,10 @@ import * as log from '../../logging/log';
|
|||
import { strictAssert } from '../../util/assert';
|
||||
import { waitForOnline } from '../../util/waitForOnline';
|
||||
import * as mapUtil from '../../util/mapUtil';
|
||||
import { isCallSafe } from '../../util/isCallSafe';
|
||||
import { isDirectConversation } from '../../util/whatTypeOfConversation';
|
||||
import { SHOW_TOAST, ToastType } from './toast';
|
||||
import type { ShowToastActionType } from './toast';
|
||||
|
||||
// State
|
||||
|
||||
|
@ -1189,6 +1193,106 @@ function setOutgoingRing(payload: boolean): SetOutgoingRingActionType {
|
|||
};
|
||||
}
|
||||
|
||||
function onOutgoingVideoCallInConversation(
|
||||
conversationId: string
|
||||
): ThunkAction<
|
||||
void,
|
||||
RootStateType,
|
||||
unknown,
|
||||
StartCallingLobbyActionType | ShowToastActionType
|
||||
> {
|
||||
return async (dispatch, getState) => {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error(
|
||||
`onOutgoingVideoCallInConversation: No conversation found for conversation ${conversationId}`
|
||||
);
|
||||
}
|
||||
|
||||
log.info('onOutgoingVideoCallInConversation: about to start a video call');
|
||||
|
||||
// if it's a group call on an announcementsOnly group
|
||||
// only allow join if the call has already been started (presumably by the admin)
|
||||
if (conversation.get('announcementsOnly') && !conversation.areWeAdmin()) {
|
||||
const call = getOwn(
|
||||
getState().calling.callsByConversation,
|
||||
conversationId
|
||||
);
|
||||
|
||||
// technically not necessary, but isAnybodyElseInGroupCall requires it
|
||||
const ourUuid = window.storage.user.getCheckedUuid().toString();
|
||||
|
||||
const isOngoingGroupCall =
|
||||
call &&
|
||||
ourUuid &&
|
||||
call.callMode === CallMode.Group &&
|
||||
call.peekInfo &&
|
||||
isAnybodyElseInGroupCall(call.peekInfo, ourUuid);
|
||||
|
||||
if (!isOngoingGroupCall) {
|
||||
dispatch({
|
||||
type: SHOW_TOAST,
|
||||
payload: {
|
||||
toastType: ToastType.CannotStartGroupCall,
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (await isCallSafe(conversation.attributes)) {
|
||||
log.info(
|
||||
'onOutgoingVideoCallInConversation: call is deemed "safe". Making call'
|
||||
);
|
||||
startCallingLobby({
|
||||
conversationId,
|
||||
isVideoCall: true,
|
||||
})(dispatch, getState, undefined);
|
||||
log.info('onOutgoingVideoCallInConversation: started the call');
|
||||
} else {
|
||||
log.info(
|
||||
'onOutgoingVideoCallInConversation: call is deemed "unsafe". Stopping'
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function onOutgoingAudioCallInConversation(
|
||||
conversationId: string
|
||||
): ThunkAction<void, RootStateType, unknown, StartCallingLobbyActionType> {
|
||||
return async (dispatch, getState) => {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error(
|
||||
`onOutgoingAudioCallInConversation: No conversation found for conversation ${conversationId}`
|
||||
);
|
||||
}
|
||||
|
||||
if (!isDirectConversation(conversation.attributes)) {
|
||||
throw new Error(
|
||||
`onOutgoingAudioCallInConversation: Conversation ${conversation.idForLogging()} is not 1:1`
|
||||
);
|
||||
}
|
||||
|
||||
log.info('onOutgoingAudioCallInConversation: about to start an audio call');
|
||||
|
||||
if (await isCallSafe(conversation.attributes)) {
|
||||
log.info(
|
||||
'onOutgoingAudioCallInConversation: call is deemed "safe". Making call'
|
||||
);
|
||||
startCallingLobby({
|
||||
conversationId,
|
||||
isVideoCall: false,
|
||||
})(dispatch, getState, undefined);
|
||||
log.info('onOutgoingAudioCallInConversation: started the call');
|
||||
} else {
|
||||
log.info(
|
||||
'onOutgoingAudioCallInConversation: call is deemed "unsafe". Stopping'
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function startCallingLobby({
|
||||
conversationId,
|
||||
isVideoCall,
|
||||
|
@ -1346,6 +1450,8 @@ export const actions = {
|
|||
hangUpActiveCall,
|
||||
keyChangeOk,
|
||||
keyChanged,
|
||||
onOutgoingVideoCallInConversation,
|
||||
onOutgoingAudioCallInConversation,
|
||||
openSystemPreferencesAction,
|
||||
outgoingCall,
|
||||
peekGroupCallForTheFirstTime,
|
||||
|
|
|
@ -897,9 +897,11 @@ export const actions = {
|
|||
setComposeGroupName,
|
||||
setComposeSearchTerm,
|
||||
setDisappearingMessages,
|
||||
setDontNotifyForMentionsIfMuted,
|
||||
setIsFetchingUUID,
|
||||
setIsNearBottom,
|
||||
setMessageLoadingState,
|
||||
setMuteExpiration,
|
||||
setPreJoinConversation,
|
||||
setSelectedConversationHeaderTitle,
|
||||
setSelectedConversationPanelDepth,
|
||||
|
@ -943,7 +945,7 @@ async function getAvatarsAndUpdateConversation(
|
|||
): Promise<Array<AvatarDataType>> {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('No conversation found');
|
||||
throw new Error('getAvatarsAndUpdateConversation: No conversation found');
|
||||
}
|
||||
|
||||
const { conversationLookup } = conversations;
|
||||
|
@ -1004,7 +1006,7 @@ function changeHasGroupLink(
|
|||
return async dispatch => {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('No conversation found');
|
||||
throw new Error('changeHasGroupLink: No conversation found');
|
||||
}
|
||||
|
||||
await longRunningTaskWrapper({
|
||||
|
@ -1026,7 +1028,7 @@ function setAnnouncementsOnly(
|
|||
return async dispatch => {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('No conversation found');
|
||||
throw new Error('setAnnouncementsOnly: No conversation found');
|
||||
}
|
||||
|
||||
await longRunningTaskWrapper({
|
||||
|
@ -1048,7 +1050,7 @@ function setAccessControlMembersSetting(
|
|||
return async dispatch => {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('No conversation found');
|
||||
throw new Error('setAccessControlMembersSetting: No conversation found');
|
||||
}
|
||||
|
||||
await longRunningTaskWrapper({
|
||||
|
@ -1070,7 +1072,9 @@ function setAccessControlAttributesSetting(
|
|||
return async dispatch => {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('No conversation found');
|
||||
throw new Error(
|
||||
'setAccessControlAttributesSetting: No conversation found'
|
||||
);
|
||||
}
|
||||
|
||||
await longRunningTaskWrapper({
|
||||
|
@ -1092,7 +1096,7 @@ function setDisappearingMessages(
|
|||
return async dispatch => {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('No conversation found');
|
||||
throw new Error('setDisappearingMessages: No conversation found');
|
||||
}
|
||||
|
||||
const valueToSet = seconds > 0 ? seconds : undefined;
|
||||
|
@ -1112,13 +1116,51 @@ function setDisappearingMessages(
|
|||
};
|
||||
}
|
||||
|
||||
function setDontNotifyForMentionsIfMuted(
|
||||
conversationId: string,
|
||||
newValue: boolean
|
||||
): NoopActionType {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('setDontNotifyForMentionsIfMuted: No conversation found');
|
||||
}
|
||||
|
||||
conversation.setDontNotifyForMentionsIfMuted(newValue);
|
||||
|
||||
return {
|
||||
type: 'NOOP',
|
||||
payload: null,
|
||||
};
|
||||
}
|
||||
|
||||
function setMuteExpiration(
|
||||
conversationId: string,
|
||||
muteExpiresAt = 0
|
||||
): NoopActionType {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('setMuteExpiration: No conversation found');
|
||||
}
|
||||
|
||||
conversation.setMuteExpiration(
|
||||
muteExpiresAt >= Number.MAX_SAFE_INTEGER
|
||||
? muteExpiresAt
|
||||
: Date.now() + muteExpiresAt
|
||||
);
|
||||
|
||||
return {
|
||||
type: 'NOOP',
|
||||
payload: null,
|
||||
};
|
||||
}
|
||||
|
||||
function destroyMessages(
|
||||
conversationId: string
|
||||
): ThunkAction<void, RootStateType, unknown, NoopActionType> {
|
||||
return async dispatch => {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('No conversation found');
|
||||
throw new Error('destroyMessages: No conversation found');
|
||||
}
|
||||
|
||||
await longRunningTaskWrapper({
|
||||
|
@ -1144,7 +1186,7 @@ function generateNewGroupLink(
|
|||
return async dispatch => {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('No conversation found');
|
||||
throw new Error('generateNewGroupLink: No conversation found');
|
||||
}
|
||||
|
||||
await longRunningTaskWrapper({
|
||||
|
@ -1217,7 +1259,9 @@ function setAccessControlAddFromInviteLinkSetting(
|
|||
return async dispatch => {
|
||||
const conversation = window.ConversationController.get(conversationId);
|
||||
if (!conversation) {
|
||||
throw new Error('No conversation found');
|
||||
throw new Error(
|
||||
'setAccessControlAddFromInviteLinkSetting: No conversation found'
|
||||
);
|
||||
}
|
||||
|
||||
await longRunningTaskWrapper({
|
||||
|
@ -1280,7 +1324,7 @@ function saveAvatarToDisk(
|
|||
): ThunkAction<void, RootStateType, unknown, ReplaceAvatarsActionType> {
|
||||
return async (dispatch, getState) => {
|
||||
if (!avatarData.buffer) {
|
||||
throw new Error('No avatar Uint8Array provided');
|
||||
throw new Error('saveAvatarToDisk: No avatar Uint8Array provided');
|
||||
}
|
||||
|
||||
strictAssert(conversationId, 'conversationId not provided');
|
||||
|
|
|
@ -5,7 +5,13 @@ import { useBoundActions } from '../../hooks/useBoundActions';
|
|||
import type { ReplacementValuesType } from '../../types/Util';
|
||||
|
||||
export enum ToastType {
|
||||
AddingUserToGroup = 'AddingUserToGroup',
|
||||
CannotStartGroupCall = 'CannotStartGroupCall',
|
||||
CopiedUsername = 'CopiedUsername',
|
||||
CopiedUsernameLink = 'CopiedUsernameLink',
|
||||
DeleteForEveryoneFailed = 'DeleteForEveryoneFailed',
|
||||
Error = 'Error',
|
||||
FailedToDeleteUsername = 'FailedToDeleteUsername',
|
||||
MessageBodyTooLong = 'MessageBodyTooLong',
|
||||
StoryMuted = 'StoryMuted',
|
||||
StoryReact = 'StoryReact',
|
||||
|
@ -13,12 +19,7 @@ export enum ToastType {
|
|||
StoryVideoError = 'StoryVideoError',
|
||||
StoryVideoTooLong = 'StoryVideoTooLong',
|
||||
StoryVideoUnsupported = 'StoryVideoUnsupported',
|
||||
AddingUserToGroup = 'AddingUserToGroup',
|
||||
UserAddedToGroup = 'UserAddedToGroup',
|
||||
FailedToDeleteUsername = 'FailedToDeleteUsername',
|
||||
CopiedUsername = 'CopiedUsername',
|
||||
CopiedUsernameLink = 'CopiedUsernameLink',
|
||||
DeleteForEveryoneFailed = 'DeleteForEveryoneFailed',
|
||||
}
|
||||
|
||||
// State
|
||||
|
|
|
@ -57,9 +57,6 @@ export type SmartConversationDetailsProps = {
|
|||
onBlock: () => void;
|
||||
onLeave: () => void;
|
||||
onUnblock: () => void;
|
||||
setMuteExpiration: (muteExpiresAt: undefined | number) => unknown;
|
||||
onOutgoingAudioCallInConversation: () => unknown;
|
||||
onOutgoingVideoCallInConversation: () => unknown;
|
||||
};
|
||||
|
||||
const ACCESS_ENUM = Proto.AccessControl.AccessRequired;
|
||||
|
|
|
@ -33,10 +33,7 @@ export type OwnProps = {
|
|||
onGoBack: () => void;
|
||||
onMarkUnread: () => void;
|
||||
onMoveToInbox: () => void;
|
||||
onOutgoingAudioCallInConversation: () => void;
|
||||
onOutgoingVideoCallInConversation: () => void;
|
||||
onSearchInConversation: () => void;
|
||||
onSetMuteNotifications: (seconds: number) => void;
|
||||
onSetPin: (value: boolean) => void;
|
||||
onShowAllMedia: () => void;
|
||||
onShowConversationDetails: () => void;
|
||||
|
|
|
@ -7,36 +7,31 @@ import type { StateType } from '../reducer';
|
|||
import { getIntl } from '../selectors/user';
|
||||
import { getConversationByIdSelector } from '../selectors/conversations';
|
||||
import { strictAssert } from '../../util/assert';
|
||||
import { mapDispatchToProps } from '../actions';
|
||||
|
||||
export type OwnProps = {
|
||||
conversationId: string;
|
||||
setDontNotifyForMentionsIfMuted: (
|
||||
dontNotifyForMentionsIfMuted: boolean
|
||||
) => unknown;
|
||||
setMuteExpiration: (muteExpiresAt: undefined | number) => unknown;
|
||||
};
|
||||
|
||||
const mapStateToProps = (state: StateType, props: OwnProps) => {
|
||||
const { conversationId, setDontNotifyForMentionsIfMuted, setMuteExpiration } =
|
||||
props;
|
||||
const { conversationId } = props;
|
||||
|
||||
const conversationSelector = getConversationByIdSelector(state);
|
||||
const conversation = conversationSelector(conversationId);
|
||||
strictAssert(conversation, 'Expected a conversation to be found');
|
||||
|
||||
return {
|
||||
id: conversationId,
|
||||
conversationType: conversation.type,
|
||||
dontNotifyForMentionsIfMuted: Boolean(
|
||||
conversation.dontNotifyForMentionsIfMuted
|
||||
),
|
||||
i18n: getIntl(state),
|
||||
muteExpiresAt: conversation.muteExpiresAt,
|
||||
setDontNotifyForMentionsIfMuted,
|
||||
setMuteExpiration,
|
||||
};
|
||||
};
|
||||
|
||||
const smart = connect(mapStateToProps, {});
|
||||
const smart = connect(mapStateToProps, mapDispatchToProps);
|
||||
|
||||
export const SmartConversationNotificationsSettings = smart(
|
||||
ConversationNotificationsSettings
|
||||
|
|
38
ts/util/isCallSafe.ts
Normal file
38
ts/util/isCallSafe.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { ConversationAttributesType } from '../model-types';
|
||||
import type { RecipientsByConversation } from '../state/ducks/stories';
|
||||
|
||||
import * as log from '../logging/log';
|
||||
import { SafetyNumberChangeSource } from '../components/SafetyNumberChangeDialog';
|
||||
import { blockSendUntilConversationsAreVerified } from './blockSendUntilConversationsAreVerified';
|
||||
import { getConversationMembers } from './getConversationMembers';
|
||||
import { UUID } from '../types/UUID';
|
||||
import { isNotNil } from './isNotNil';
|
||||
|
||||
export async function isCallSafe(
|
||||
attributes: ConversationAttributesType
|
||||
): Promise<boolean> {
|
||||
const recipientsByConversation: RecipientsByConversation = {
|
||||
[attributes.id]: {
|
||||
uuids: getConversationMembers(attributes)
|
||||
.map(member =>
|
||||
member.uuid ? UUID.checkedLookup(member.uuid).toString() : undefined
|
||||
)
|
||||
.filter(isNotNil),
|
||||
},
|
||||
};
|
||||
|
||||
const callAnyway = await blockSendUntilConversationsAreVerified(
|
||||
recipientsByConversation,
|
||||
SafetyNumberChangeSource.Calling
|
||||
);
|
||||
|
||||
if (!callAnyway) {
|
||||
log.info('Safety number change dialog not accepted, new call not allowed.');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -13,7 +13,6 @@ import type {
|
|||
ToastCannotOpenGiftBadge,
|
||||
ToastPropsType as ToastCannotOpenGiftBadgePropsType,
|
||||
} from '../components/ToastCannotOpenGiftBadge';
|
||||
import type { ToastCannotStartGroupCall } from '../components/ToastCannotStartGroupCall';
|
||||
import type { ToastCaptchaFailed } from '../components/ToastCaptchaFailed';
|
||||
import type { ToastCaptchaSolved } from '../components/ToastCaptchaSolved';
|
||||
import type {
|
||||
|
@ -60,7 +59,6 @@ export function showToast(Toast: typeof ToastAlreadyRequestedToJoin): void;
|
|||
export function showToast(Toast: typeof ToastBlocked): void;
|
||||
export function showToast(Toast: typeof ToastBlockedGroup): void;
|
||||
export function showToast(Toast: typeof ToastUnsupportedMultiAttachment): void;
|
||||
export function showToast(Toast: typeof ToastCannotStartGroupCall): void;
|
||||
export function showToast(
|
||||
Toast: typeof ToastCannotOpenGiftBadge,
|
||||
props: Omit<ToastCannotOpenGiftBadgePropsType, 'i18n' | 'onClose'>
|
||||
|
|
|
@ -61,7 +61,6 @@ import { SignalService as Proto } from '../protobuf';
|
|||
import { ToastBlocked } from '../components/ToastBlocked';
|
||||
import { ToastBlockedGroup } from '../components/ToastBlockedGroup';
|
||||
import { ToastCannotMixMultiAndNonMultiAttachments } from '../components/ToastCannotMixMultiAndNonMultiAttachments';
|
||||
import { ToastCannotStartGroupCall } from '../components/ToastCannotStartGroupCall';
|
||||
import { ToastConversationArchived } from '../components/ToastConversationArchived';
|
||||
import { ToastConversationMarkedUnread } from '../components/ToastConversationMarkedUnread';
|
||||
import { ToastConversationUnarchived } from '../components/ToastConversationUnarchived';
|
||||
|
@ -108,10 +107,8 @@ import { saveAttachment } from '../util/saveAttachment';
|
|||
import { SECOND } from '../util/durations';
|
||||
import { blockSendUntilConversationsAreVerified } from '../util/blockSendUntilConversationsAreVerified';
|
||||
import { SafetyNumberChangeSource } from '../components/SafetyNumberChangeDialog';
|
||||
import { getOwn } from '../util/getOwn';
|
||||
import { CallMode } from '../types/Calling';
|
||||
import { isAnybodyElseInGroupCall } from '../state/ducks/calling';
|
||||
import { startConversation } from '../util/startConversation';
|
||||
import { longRunningTaskWrapper } from '../util/longRunningTaskWrapper';
|
||||
|
||||
type AttachmentOptions = {
|
||||
messageId: string;
|
||||
|
@ -306,12 +303,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
return this;
|
||||
}
|
||||
|
||||
setMuteExpiration(ms = 0): void {
|
||||
this.model.setMuteExpiration(
|
||||
ms >= Number.MAX_SAFE_INTEGER ? ms : Date.now() + ms
|
||||
);
|
||||
}
|
||||
|
||||
setPin(value: boolean): void {
|
||||
if (value) {
|
||||
const pinnedConversationIds = window.storage.get(
|
||||
|
@ -338,15 +329,7 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
const { searchInConversation } = window.reduxActions.search;
|
||||
searchInConversation(this.model.id);
|
||||
},
|
||||
onSetMuteNotifications: this.setMuteExpiration.bind(this),
|
||||
onSetPin: this.setPin.bind(this),
|
||||
// These are view only and don't update the Conversation model, so they
|
||||
// need a manual update call.
|
||||
onOutgoingAudioCallInConversation:
|
||||
this.onOutgoingAudioCallInConversation.bind(this),
|
||||
onOutgoingVideoCallInConversation:
|
||||
this.onOutgoingVideoCallInConversation.bind(this),
|
||||
|
||||
onShowConversationDetails: () => {
|
||||
this.showConversationDetails();
|
||||
},
|
||||
|
@ -505,7 +488,8 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
messageRequestEnum.ACCEPT
|
||||
),
|
||||
removeMember: (conversationId: string) => {
|
||||
this.longRunningTaskWrapper({
|
||||
longRunningTaskWrapper({
|
||||
idForLogging: this.model.idForLogging(),
|
||||
name: 'removeMember',
|
||||
task: () => this.model.removeFromGroupV2(conversationId),
|
||||
});
|
||||
|
@ -575,7 +559,8 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
okText: window.i18n('GroupV2--join--cancel-request-to-join--yes'),
|
||||
cancelText: window.i18n('GroupV2--join--cancel-request-to-join--no'),
|
||||
resolve: () => {
|
||||
this.longRunningTaskWrapper({
|
||||
longRunningTaskWrapper({
|
||||
idForLogging: this.model.idForLogging(),
|
||||
name: 'onCancelJoinRequest',
|
||||
task: async () => this.model.cancelJoinRequest(),
|
||||
});
|
||||
|
@ -630,83 +615,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
this.$('.ConversationView__template').append(this.conversationView.el);
|
||||
}
|
||||
|
||||
async onOutgoingVideoCallInConversation(): Promise<void> {
|
||||
log.info('onOutgoingVideoCallInConversation: about to start a video call');
|
||||
|
||||
// if it's a group call on an announcementsOnly group
|
||||
// only allow join if the call has already been started (presumably by the admin)
|
||||
if (this.model.get('announcementsOnly') && !this.model.areWeAdmin()) {
|
||||
const call = getOwn(
|
||||
window.reduxStore.getState().calling.callsByConversation,
|
||||
this.model.id
|
||||
);
|
||||
|
||||
// technically not necessary, but isAnybodyElseInGroupCall requires it
|
||||
const ourUuid = window.storage.user.getCheckedUuid().toString();
|
||||
|
||||
const isOngoingGroupCall =
|
||||
call &&
|
||||
ourUuid &&
|
||||
call.callMode === CallMode.Group &&
|
||||
call.peekInfo &&
|
||||
isAnybodyElseInGroupCall(call.peekInfo, ourUuid);
|
||||
|
||||
if (!isOngoingGroupCall) {
|
||||
showToast(ToastCannotStartGroupCall);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (await this.isCallSafe()) {
|
||||
log.info(
|
||||
'onOutgoingVideoCallInConversation: call is deemed "safe". Making call'
|
||||
);
|
||||
window.reduxActions.calling.startCallingLobby({
|
||||
conversationId: this.model.id,
|
||||
isVideoCall: true,
|
||||
});
|
||||
log.info('onOutgoingVideoCallInConversation: started the call');
|
||||
} else {
|
||||
log.info(
|
||||
'onOutgoingVideoCallInConversation: call is deemed "unsafe". Stopping'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async onOutgoingAudioCallInConversation(): Promise<void> {
|
||||
log.info('onOutgoingAudioCallInConversation: about to start an audio call');
|
||||
|
||||
if (await this.isCallSafe()) {
|
||||
log.info(
|
||||
'onOutgoingAudioCallInConversation: call is deemed "safe". Making call'
|
||||
);
|
||||
window.reduxActions.calling.startCallingLobby({
|
||||
conversationId: this.model.id,
|
||||
isVideoCall: false,
|
||||
});
|
||||
log.info('onOutgoingAudioCallInConversation: started the call');
|
||||
} else {
|
||||
log.info(
|
||||
'onOutgoingAudioCallInConversation: call is deemed "unsafe". Stopping'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async longRunningTaskWrapper<T>({
|
||||
name,
|
||||
task,
|
||||
}: {
|
||||
name: string;
|
||||
task: () => Promise<T>;
|
||||
}): Promise<T> {
|
||||
const idForLogging = this.model.idForLogging();
|
||||
return window.Signal.Util.longRunningTaskWrapper({
|
||||
name,
|
||||
idForLogging,
|
||||
task,
|
||||
});
|
||||
}
|
||||
|
||||
getMessageActions(): MessageActionsType {
|
||||
const reactToMessage = async (
|
||||
messageId: string,
|
||||
|
@ -896,7 +804,8 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
const migrate = () => {
|
||||
onClose();
|
||||
|
||||
this.longRunningTaskWrapper({
|
||||
longRunningTaskWrapper({
|
||||
idForLogging: this.model.idForLogging(),
|
||||
name: 'initiateMigrationToGroupV2',
|
||||
task: () => window.Signal.Groups.initiateMigrationToGroupV2(this.model),
|
||||
});
|
||||
|
@ -905,7 +814,8 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
// Note: this call will throw if, after generating member lists, we are no longer a
|
||||
// member or are in the pending member list.
|
||||
const { droppedGV2MemberIds, pendingMembersV2 } =
|
||||
await this.longRunningTaskWrapper({
|
||||
await longRunningTaskWrapper({
|
||||
idForLogging: this.model.idForLogging(),
|
||||
name: 'getGroupMigrationMembers',
|
||||
task: () => window.Signal.Groups.getGroupMigrationMembers(this.model),
|
||||
});
|
||||
|
@ -1103,7 +1013,8 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
model: ConversationModel,
|
||||
messageRequestType: number
|
||||
): Promise<void> {
|
||||
return this.longRunningTaskWrapper({
|
||||
return longRunningTaskWrapper({
|
||||
idForLogging: this.model.idForLogging(),
|
||||
name,
|
||||
task: model.syncMessageRequestResponse.bind(model, messageRequestType),
|
||||
});
|
||||
|
@ -1112,7 +1023,8 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
blockAndReportSpam(model: ConversationModel): Promise<void> {
|
||||
const messageRequestEnum = Proto.SyncMessage.MessageRequestResponse.Type;
|
||||
|
||||
return this.longRunningTaskWrapper({
|
||||
return longRunningTaskWrapper({
|
||||
idForLogging: this.model.idForLogging(),
|
||||
name: 'blockAndReportSpam',
|
||||
task: async () => {
|
||||
await Promise.all([
|
||||
|
@ -1862,9 +1774,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
window.reduxStore,
|
||||
{
|
||||
conversationId: this.model.id,
|
||||
setDontNotifyForMentionsIfMuted:
|
||||
this.model.setDontNotifyForMentionsIfMuted.bind(this.model),
|
||||
setMuteExpiration: this.setMuteExpiration.bind(this),
|
||||
}
|
||||
),
|
||||
});
|
||||
|
@ -1900,7 +1809,8 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
// dried up and hoisted to methods on ConversationView
|
||||
|
||||
const onLeave = () => {
|
||||
this.longRunningTaskWrapper({
|
||||
longRunningTaskWrapper({
|
||||
idForLogging: this.model.idForLogging(),
|
||||
name: 'onLeave',
|
||||
task: () => this.model.leaveGroupV2(),
|
||||
});
|
||||
|
@ -1938,11 +1848,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
messageRequestEnum.ACCEPT
|
||||
);
|
||||
},
|
||||
setMuteExpiration: this.setMuteExpiration.bind(this),
|
||||
onOutgoingAudioCallInConversation:
|
||||
this.onOutgoingAudioCallInConversation.bind(this),
|
||||
onOutgoingVideoCallInConversation:
|
||||
this.onOutgoingVideoCallInConversation.bind(this),
|
||||
};
|
||||
|
||||
const view = new ReactWrapperView({
|
||||
|
@ -2128,28 +2033,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
);
|
||||
}
|
||||
|
||||
async isCallSafe(): Promise<boolean> {
|
||||
const recipientsByConversation = {
|
||||
[this.model.id]: {
|
||||
uuids: this.model.getMemberUuids().map(uuid => uuid.toString()),
|
||||
},
|
||||
};
|
||||
|
||||
const callAnyway = await blockSendUntilConversationsAreVerified(
|
||||
recipientsByConversation,
|
||||
SafetyNumberChangeSource.Calling
|
||||
);
|
||||
|
||||
if (!callAnyway) {
|
||||
log.info(
|
||||
'Safety number change dialog not accepted, new call not allowed.'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async sendStickerMessage(options: {
|
||||
packId: string;
|
||||
stickerId: number;
|
||||
|
|
Loading…
Reference in a new issue