Move getUntrustedContacts out of conversation_view
This commit is contained in:
parent
96c4cc4bcf
commit
936ce91b2e
19 changed files with 410 additions and 414 deletions
|
@ -52,11 +52,8 @@ type PropsType = {
|
|||
|
||||
export const App = ({
|
||||
appView,
|
||||
cancelConversationVerification,
|
||||
conversationsStoppingSend,
|
||||
executeMenuAction,
|
||||
executeMenuRole,
|
||||
getPreferredBadge,
|
||||
hasInitialLoadCompleted,
|
||||
hasSelectedStoryData,
|
||||
hideMenuBar,
|
||||
|
@ -75,7 +72,6 @@ export const App = ({
|
|||
renderCustomizingPreferredReactionsModal,
|
||||
renderGlobalModalContainer,
|
||||
renderLeftPane,
|
||||
renderSafetyNumber,
|
||||
renderStories,
|
||||
renderStoryViewer,
|
||||
requestVerification,
|
||||
|
@ -86,7 +82,6 @@ export const App = ({
|
|||
theme,
|
||||
titleBarDoubleClick,
|
||||
toastType,
|
||||
verifyConversationsStoppingSend,
|
||||
}: PropsType): JSX.Element => {
|
||||
let contents;
|
||||
|
||||
|
@ -107,23 +102,17 @@ export const App = ({
|
|||
} else if (appView === AppViewType.Inbox) {
|
||||
contents = (
|
||||
<Inbox
|
||||
cancelConversationVerification={cancelConversationVerification}
|
||||
conversationsStoppingSend={conversationsStoppingSend}
|
||||
hasInitialLoadCompleted={hasInitialLoadCompleted}
|
||||
getPreferredBadge={getPreferredBadge}
|
||||
i18n={i18n}
|
||||
isCustomizingPreferredReactions={isCustomizingPreferredReactions}
|
||||
renderCustomizingPreferredReactionsModal={
|
||||
renderCustomizingPreferredReactionsModal
|
||||
}
|
||||
renderLeftPane={renderLeftPane}
|
||||
renderSafetyNumber={renderSafetyNumber}
|
||||
selectedConversationId={selectedConversationId}
|
||||
selectedMessage={selectedMessage}
|
||||
showConversation={showConversation}
|
||||
showWhatsNewModal={showWhatsNewModal}
|
||||
theme={theme}
|
||||
verifyConversationsStoppingSend={verifyConversationsStoppingSend}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ export type OwnProps = Readonly<{
|
|||
hasXButton?: boolean;
|
||||
i18n: LocalizerType;
|
||||
moduleClassName?: string;
|
||||
noMouseClose?: boolean;
|
||||
onCancel?: () => unknown;
|
||||
onClose: () => unknown;
|
||||
onTopOfEverything?: boolean;
|
||||
|
@ -56,18 +57,19 @@ function getButtonVariant(
|
|||
|
||||
export const ConfirmationDialog = React.memo(
|
||||
({
|
||||
moduleClassName,
|
||||
actions = [],
|
||||
cancelButtonVariant,
|
||||
cancelText,
|
||||
children,
|
||||
hasXButton,
|
||||
i18n,
|
||||
moduleClassName,
|
||||
noMouseClose,
|
||||
onCancel,
|
||||
onClose,
|
||||
onTopOfEverything,
|
||||
theme,
|
||||
title,
|
||||
hasXButton,
|
||||
cancelButtonVariant,
|
||||
onTopOfEverything,
|
||||
}: Props) => {
|
||||
const { close, overlayStyles, modalStyles } = useAnimated(onClose, {
|
||||
getFrom: () => ({ opacity: 0, transform: 'scale(0.25)' }),
|
||||
|
@ -94,10 +96,11 @@ export const ConfirmationDialog = React.memo(
|
|||
|
||||
return (
|
||||
<ModalHost
|
||||
onTopOfEverything={onTopOfEverything}
|
||||
noMouseClose={noMouseClose}
|
||||
onClose={close}
|
||||
theme={theme}
|
||||
onTopOfEverything={onTopOfEverything}
|
||||
overlayStyles={overlayStyles}
|
||||
theme={theme}
|
||||
>
|
||||
<animated.div style={modalStyles}>
|
||||
<ModalWindow
|
||||
|
|
|
@ -6,6 +6,7 @@ import type {
|
|||
ContactModalStateType,
|
||||
ForwardMessagePropsType,
|
||||
UserNotFoundModalStateType,
|
||||
SafetyNumberChangedBlockingDataType,
|
||||
} from '../state/ducks/globalModals';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
import { missingCaseError } from '../util/missingCaseError';
|
||||
|
@ -35,6 +36,10 @@ type PropsType = {
|
|||
// StoriesSettings
|
||||
isStoriesSettingsVisible: boolean;
|
||||
renderStoriesSettings: () => JSX.Element;
|
||||
// SendAnywayDialog
|
||||
hasSafetyNumberChangeModal: boolean;
|
||||
safetyNumberChangedBlockingData?: SafetyNumberChangedBlockingDataType;
|
||||
renderSendAnywayDialog: () => JSX.Element;
|
||||
// UserNotFoundModal
|
||||
hideUserNotFoundModal: () => unknown;
|
||||
userNotFoundModalState?: UserNotFoundModalStateType;
|
||||
|
@ -63,6 +68,10 @@ export const GlobalModalContainer = ({
|
|||
// StoriesSettings
|
||||
isStoriesSettingsVisible,
|
||||
renderStoriesSettings,
|
||||
// SendAnywayDialog
|
||||
hasSafetyNumberChangeModal,
|
||||
safetyNumberChangedBlockingData,
|
||||
renderSendAnywayDialog,
|
||||
// UserNotFoundModal
|
||||
hideUserNotFoundModal,
|
||||
userNotFoundModalState,
|
||||
|
@ -70,6 +79,12 @@ export const GlobalModalContainer = ({
|
|||
hideWhatsNewModal,
|
||||
isWhatsNewVisible,
|
||||
}: PropsType): JSX.Element | null => {
|
||||
// We want the send anyway dialog to supersede most modals since this is an
|
||||
// immediate action the user needs to take.
|
||||
if (hasSafetyNumberChangeModal || safetyNumberChangedBlockingData) {
|
||||
return renderSendAnywayDialog();
|
||||
}
|
||||
|
||||
if (safetyNumberModalContactId) {
|
||||
return renderSafetyNumber();
|
||||
}
|
||||
|
|
|
@ -5,57 +5,39 @@ import type { ReactNode } from 'react';
|
|||
import React, { useEffect, useRef, useState } from 'react';
|
||||
|
||||
import type { ConversationModel } from '../models/conversations';
|
||||
import type {
|
||||
ConversationType,
|
||||
ShowConversationType,
|
||||
} from '../state/ducks/conversations';
|
||||
import type { ShowConversationType } from '../state/ducks/conversations';
|
||||
import type { ConversationView } from '../views/conversation_view';
|
||||
import type { LocalizerType, ThemeType } from '../types/Util';
|
||||
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
||||
import type { SafetyNumberProps } from './SafetyNumberChangeDialog';
|
||||
import type { LocalizerType } from '../types/Util';
|
||||
|
||||
import * as log from '../logging/log';
|
||||
import { SECOND } from '../util/durations';
|
||||
import { SafetyNumberChangeDialog } from './SafetyNumberChangeDialog';
|
||||
import { ToastStickerPackInstallFailed } from './ToastStickerPackInstallFailed';
|
||||
import { WhatsNewLink } from './WhatsNewLink';
|
||||
import { showToast } from '../util/showToast';
|
||||
import { strictAssert } from '../util/assert';
|
||||
|
||||
export type PropsType = {
|
||||
cancelConversationVerification: () => void;
|
||||
conversationsStoppingSend: Array<ConversationType>;
|
||||
hasInitialLoadCompleted: boolean;
|
||||
getPreferredBadge: PreferredBadgeSelectorType;
|
||||
i18n: LocalizerType;
|
||||
isCustomizingPreferredReactions: boolean;
|
||||
renderCustomizingPreferredReactionsModal: () => JSX.Element;
|
||||
renderLeftPane: () => JSX.Element;
|
||||
renderSafetyNumber: (props: SafetyNumberProps) => JSX.Element;
|
||||
selectedConversationId?: string;
|
||||
selectedMessage?: string;
|
||||
showConversation: ShowConversationType;
|
||||
showWhatsNewModal: () => unknown;
|
||||
theme: ThemeType;
|
||||
verifyConversationsStoppingSend: () => void;
|
||||
};
|
||||
|
||||
export const Inbox = ({
|
||||
cancelConversationVerification,
|
||||
conversationsStoppingSend,
|
||||
hasInitialLoadCompleted,
|
||||
getPreferredBadge,
|
||||
i18n,
|
||||
isCustomizingPreferredReactions,
|
||||
renderCustomizingPreferredReactionsModal,
|
||||
renderLeftPane,
|
||||
renderSafetyNumber,
|
||||
selectedConversationId,
|
||||
selectedMessage,
|
||||
showConversation,
|
||||
showWhatsNewModal,
|
||||
theme,
|
||||
verifyConversationsStoppingSend,
|
||||
}: PropsType): JSX.Element => {
|
||||
const [loadingMessageCount, setLoadingMessageCount] = useState(0);
|
||||
const [internalHasInitialLoadCompleted, setInternalHasInitialLoadCompleted] =
|
||||
|
@ -226,21 +208,7 @@ export const Inbox = ({
|
|||
}
|
||||
|
||||
let activeModal: ReactNode;
|
||||
if (conversationsStoppingSend.length) {
|
||||
activeModal = (
|
||||
<SafetyNumberChangeDialog
|
||||
confirmText={i18n('safetyNumberChangeDialog__pending-messages')}
|
||||
contacts={conversationsStoppingSend}
|
||||
getPreferredBadge={getPreferredBadge}
|
||||
i18n={i18n}
|
||||
onCancel={cancelConversationVerification}
|
||||
onConfirm={verifyConversationsStoppingSend}
|
||||
renderSafetyNumber={renderSafetyNumber}
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (!activeModal && isCustomizingPreferredReactions) {
|
||||
if (isCustomizingPreferredReactions) {
|
||||
activeModal = renderCustomizingPreferredReactionsModal();
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,11 @@ import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
|||
import type { LocalizerType, ThemeType } from '../types/Util';
|
||||
import { isInSystemContacts } from '../util/isInSystemContacts';
|
||||
|
||||
export enum SafetyNumberChangeSource {
|
||||
Calling = 'Calling',
|
||||
MessageSend = 'MessageSend',
|
||||
}
|
||||
|
||||
export type SafetyNumberProps = {
|
||||
contactID: string;
|
||||
onClose: () => void;
|
||||
|
@ -75,6 +80,7 @@ export const SafetyNumberChangeDialog = ({
|
|||
},
|
||||
]}
|
||||
i18n={i18n}
|
||||
noMouseClose
|
||||
onCancel={onClose}
|
||||
onClose={noop}
|
||||
title={i18n('safetyNumberChanges')}
|
||||
|
|
44
ts/services/singleServePromise.ts
Normal file
44
ts/services/singleServePromise.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { ExplodePromiseResultType } from '../util/explodePromise';
|
||||
import type { UUIDStringType } from '../types/UUID';
|
||||
import { UUID } from '../types/UUID';
|
||||
|
||||
// This module provides single serve promises in a pub/sub manner.
|
||||
// One example usage is if you're calling a redux action creator but need to
|
||||
// await some result within it, you may pass in this promise and access it in
|
||||
// other parts of the app via its referencing UUID.
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const promises = new Map<UUIDStringType, ExplodePromiseResultType<any>>();
|
||||
|
||||
export function set<T>(
|
||||
explodedPromise: ExplodePromiseResultType<T>
|
||||
): UUIDStringType {
|
||||
let uuid = UUID.generate().toString();
|
||||
|
||||
while (promises.has(uuid)) {
|
||||
uuid = UUID.generate().toString();
|
||||
}
|
||||
|
||||
promises.set(uuid, {
|
||||
promise: explodedPromise.promise,
|
||||
resolve: value => {
|
||||
promises.delete(uuid);
|
||||
explodedPromise.resolve(value);
|
||||
},
|
||||
reject: err => {
|
||||
promises.delete(uuid);
|
||||
explodedPromise.reject(err);
|
||||
},
|
||||
});
|
||||
|
||||
return uuid;
|
||||
}
|
||||
|
||||
export function get<T>(
|
||||
uuid: UUIDStringType
|
||||
): ExplodePromiseResultType<T> | undefined {
|
||||
return promises.get(uuid);
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
// This file is here temporarily while we're switching off of Backbone into
|
||||
// React. In the future, and in React-land, please just import and use
|
||||
// the component directly. This is the thin API layer to bridge the gap
|
||||
// while we convert things over. Please delete this file once all usages are
|
||||
// ported over.
|
||||
|
||||
import React from 'react';
|
||||
import { unmountComponentAtNode, render } from 'react-dom';
|
||||
import type { ConversationModel } from '../models/conversations';
|
||||
import { SafetyNumberChangeDialog } from '../components/SafetyNumberChangeDialog';
|
||||
import { getPreferredBadgeSelector } from '../state/selectors/badges';
|
||||
import { getTheme } from '../state/selectors/user';
|
||||
|
||||
export type SafetyNumberChangeViewProps = {
|
||||
confirmText?: string;
|
||||
contacts: Array<ConversationModel>;
|
||||
reject: () => void;
|
||||
resolve: () => void;
|
||||
};
|
||||
|
||||
let dialogContainerNode: HTMLElement | undefined;
|
||||
|
||||
function removeDialog() {
|
||||
if (!dialogContainerNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
unmountComponentAtNode(dialogContainerNode);
|
||||
document.body.removeChild(dialogContainerNode);
|
||||
|
||||
dialogContainerNode = undefined;
|
||||
}
|
||||
|
||||
export function showSafetyNumberChangeDialog(
|
||||
options: SafetyNumberChangeViewProps
|
||||
): void {
|
||||
if (dialogContainerNode) {
|
||||
removeDialog();
|
||||
}
|
||||
|
||||
dialogContainerNode = document.createElement('div');
|
||||
document.body.appendChild(dialogContainerNode);
|
||||
|
||||
const reduxState = window.reduxStore.getState();
|
||||
const getPreferredBadge = getPreferredBadgeSelector(reduxState);
|
||||
const theme = getTheme(reduxState);
|
||||
|
||||
render(
|
||||
<SafetyNumberChangeDialog
|
||||
confirmText={options.confirmText}
|
||||
contacts={options.contacts.map(contact => contact.format())}
|
||||
getPreferredBadge={getPreferredBadge}
|
||||
i18n={window.i18n}
|
||||
onCancel={() => {
|
||||
options.reject();
|
||||
removeDialog();
|
||||
}}
|
||||
onConfirm={() => {
|
||||
options.resolve();
|
||||
removeDialog();
|
||||
}}
|
||||
renderSafetyNumber={props => {
|
||||
return window.Signal.State.Roots.createSafetyNumberViewer(
|
||||
window.reduxStore,
|
||||
props
|
||||
);
|
||||
}}
|
||||
theme={theme}
|
||||
/>,
|
||||
dialogContainerNode
|
||||
);
|
||||
}
|
|
@ -21,8 +21,14 @@ import { calling } from '../../services/calling';
|
|||
import { getOwn } from '../../util/getOwn';
|
||||
import { assert, strictAssert } from '../../util/assert';
|
||||
import * as universalExpireTimer from '../../util/universalExpireTimer';
|
||||
import type { ToggleProfileEditorErrorActionType } from './globalModals';
|
||||
import { TOGGLE_PROFILE_EDITOR_ERROR } from './globalModals';
|
||||
import type {
|
||||
ShowSendAnywayDialogActiontype,
|
||||
ToggleProfileEditorErrorActionType,
|
||||
} from './globalModals';
|
||||
import {
|
||||
SHOW_SEND_ANYWAY_DIALOG,
|
||||
TOGGLE_PROFILE_EDITOR_ERROR,
|
||||
} from './globalModals';
|
||||
import { isRecord } from '../../util/isRecord';
|
||||
import type {
|
||||
UUIDFetchStateKeyType,
|
||||
|
@ -782,6 +788,7 @@ export type ConversationActionType =
|
|||
| ShowArchivedConversationsActionType
|
||||
| ShowChooseGroupMembersActionType
|
||||
| ShowInboxActionType
|
||||
| ShowSendAnywayDialogActiontype
|
||||
| StartComposingActionType
|
||||
| StartSettingGroupMetadataActionType
|
||||
| ToggleConversationInChooseMembersActionType
|
||||
|
@ -2162,6 +2169,45 @@ function closeComposerModal(
|
|||
};
|
||||
}
|
||||
|
||||
function getVerificationDataForConversation(
|
||||
state: Readonly<ConversationsStateType>,
|
||||
conversationId: string,
|
||||
untrustedUuids: ReadonlyArray<string>
|
||||
): Record<string, ConversationVerificationData> {
|
||||
const { verificationDataByConversation } = state;
|
||||
const existingPendingState = getOwn(
|
||||
verificationDataByConversation,
|
||||
conversationId
|
||||
);
|
||||
|
||||
if (
|
||||
!existingPendingState ||
|
||||
existingPendingState.type ===
|
||||
ConversationVerificationState.VerificationCancelled
|
||||
) {
|
||||
return {
|
||||
[conversationId]: {
|
||||
type: ConversationVerificationState.PendingVerification as const,
|
||||
uuidsNeedingVerification: untrustedUuids,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const uuidsNeedingVerification: ReadonlyArray<string> = Array.from(
|
||||
new Set([
|
||||
...existingPendingState.uuidsNeedingVerification,
|
||||
...untrustedUuids,
|
||||
])
|
||||
);
|
||||
|
||||
return {
|
||||
[conversationId]: {
|
||||
type: ConversationVerificationState.PendingVerification as const,
|
||||
uuidsNeedingVerification,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function reducer(
|
||||
state: Readonly<ConversationsStateType> = getEmptyState(),
|
||||
action: Readonly<ConversationActionType>
|
||||
|
@ -2510,47 +2556,41 @@ export function reducer(
|
|||
if (action.type === CONVERSATION_STOPPED_BY_MISSING_VERIFICATION) {
|
||||
const { conversationId, untrustedUuids } = action.payload;
|
||||
|
||||
const { verificationDataByConversation } = state;
|
||||
const existingPendingState = getOwn(
|
||||
verificationDataByConversation,
|
||||
conversationId
|
||||
);
|
||||
|
||||
if (
|
||||
!existingPendingState ||
|
||||
existingPendingState.type ===
|
||||
ConversationVerificationState.VerificationCancelled
|
||||
) {
|
||||
return {
|
||||
...state,
|
||||
verificationDataByConversation: {
|
||||
...verificationDataByConversation,
|
||||
[conversationId]: {
|
||||
type: ConversationVerificationState.PendingVerification as const,
|
||||
uuidsNeedingVerification: untrustedUuids,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const uuidsNeedingVerification: ReadonlyArray<string> = Array.from(
|
||||
new Set([
|
||||
...existingPendingState.uuidsNeedingVerification,
|
||||
...untrustedUuids,
|
||||
])
|
||||
const nextVerificationData = getVerificationDataForConversation(
|
||||
state,
|
||||
conversationId,
|
||||
untrustedUuids
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
verificationDataByConversation: {
|
||||
...verificationDataByConversation,
|
||||
[conversationId]: {
|
||||
type: ConversationVerificationState.PendingVerification as const,
|
||||
uuidsNeedingVerification,
|
||||
},
|
||||
...state.verificationDataByConversation,
|
||||
...nextVerificationData,
|
||||
},
|
||||
};
|
||||
}
|
||||
if (action.type === SHOW_SEND_ANYWAY_DIALOG) {
|
||||
const verificationDataByConversation = {
|
||||
...state.verificationDataByConversation,
|
||||
};
|
||||
|
||||
action.payload.conversationsToPause.forEach(
|
||||
(untrustedUuids, conversationId) => {
|
||||
const nextVerificationData = getVerificationDataForConversation(
|
||||
state,
|
||||
conversationId,
|
||||
Array.from(untrustedUuids)
|
||||
);
|
||||
Object.assign(verificationDataByConversation, nextVerificationData);
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
...state,
|
||||
verificationDataByConversation,
|
||||
};
|
||||
}
|
||||
if (action.type === 'MESSAGE_CHANGED') {
|
||||
const { id, conversationId, data } = action.payload;
|
||||
const existingConversation = state.messagesByConversation[conversationId];
|
||||
|
|
|
@ -2,8 +2,12 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { ThunkAction } from 'redux-thunk';
|
||||
import type { StateType as RootStateType } from '../reducer';
|
||||
import type { ExplodePromiseResultType } from '../../util/explodePromise';
|
||||
import type { PropsForMessage } from '../selectors/message';
|
||||
import type { SafetyNumberChangeSource } from '../../components/SafetyNumberChangeDialog';
|
||||
import type { StateType as RootStateType } from '../reducer';
|
||||
import type { UUIDStringType } from '../../types/UUID';
|
||||
import * as SingleServePromise from '../../services/singleServePromise';
|
||||
import { getMessageById } from '../../messages/getMessageById';
|
||||
import { getMessagePropsSelector } from '../selectors/message';
|
||||
import { useBoundActions } from '../../hooks/useBoundActions';
|
||||
|
@ -11,15 +15,20 @@ import { useBoundActions } from '../../hooks/useBoundActions';
|
|||
// State
|
||||
|
||||
export type ForwardMessagePropsType = Omit<PropsForMessage, 'renderingContext'>;
|
||||
export type SafetyNumberChangedBlockingDataType = {
|
||||
readonly promiseUuid: UUIDStringType;
|
||||
readonly source?: SafetyNumberChangeSource;
|
||||
};
|
||||
|
||||
export type GlobalModalsStateType = {
|
||||
readonly contactModalState?: ContactModalStateType;
|
||||
readonly forwardMessageProps?: ForwardMessagePropsType;
|
||||
readonly isProfileEditorVisible: boolean;
|
||||
readonly isStoriesSettingsVisible: boolean;
|
||||
readonly isSignalConnectionsVisible: boolean;
|
||||
readonly isStoriesSettingsVisible: boolean;
|
||||
readonly isWhatsNewVisible: boolean;
|
||||
readonly profileEditorHasError: boolean;
|
||||
readonly safetyNumberChangedBlockingData?: SafetyNumberChangedBlockingDataType;
|
||||
readonly safetyNumberModalContactId?: string;
|
||||
readonly userNotFoundModalState?: UserNotFoundModalStateType;
|
||||
};
|
||||
|
@ -42,6 +51,8 @@ export const TOGGLE_PROFILE_EDITOR_ERROR =
|
|||
const TOGGLE_SAFETY_NUMBER_MODAL = 'globalModals/TOGGLE_SAFETY_NUMBER_MODAL';
|
||||
const TOGGLE_SIGNAL_CONNECTIONS_MODAL =
|
||||
'globalModals/TOGGLE_SIGNAL_CONNECTIONS_MODAL';
|
||||
export const SHOW_SEND_ANYWAY_DIALOG = 'globalModals/SHOW_SEND_ANYWAY_DIALOG';
|
||||
const HIDE_SEND_ANYWAY_DIALOG = 'globalModals/HIDE_SEND_ANYWAY_DIALOG';
|
||||
|
||||
export type ContactModalStateType = {
|
||||
contactId: string;
|
||||
|
@ -114,6 +125,17 @@ type HideStoriesSettingsActionType = {
|
|||
type: typeof HIDE_STORIES_SETTINGS;
|
||||
};
|
||||
|
||||
export type ShowSendAnywayDialogActiontype = {
|
||||
type: typeof SHOW_SEND_ANYWAY_DIALOG;
|
||||
payload: SafetyNumberChangedBlockingDataType & {
|
||||
conversationsToPause: Map<string, Set<string>>;
|
||||
};
|
||||
};
|
||||
|
||||
type HideSendAnywayDialogActiontype = {
|
||||
type: typeof HIDE_SEND_ANYWAY_DIALOG;
|
||||
};
|
||||
|
||||
export type GlobalModalsActionType =
|
||||
| HideContactModalActionType
|
||||
| ShowContactModalActionType
|
||||
|
@ -123,6 +145,8 @@ export type GlobalModalsActionType =
|
|||
| ShowUserNotFoundModalActionType
|
||||
| HideStoriesSettingsActionType
|
||||
| ShowStoriesSettingsActionType
|
||||
| HideSendAnywayDialogActiontype
|
||||
| ShowSendAnywayDialogActiontype
|
||||
| ToggleForwardMessageModalActionType
|
||||
| ToggleProfileEditorActionType
|
||||
| ToggleProfileEditorErrorActionType
|
||||
|
@ -140,6 +164,8 @@ export const actions = {
|
|||
showUserNotFoundModal,
|
||||
hideStoriesSettings,
|
||||
showStoriesSettings,
|
||||
hideBlockingSafetyNumberChangeDialog,
|
||||
showBlockingSafetyNumberChangeDialog,
|
||||
toggleForwardMessageModal,
|
||||
toggleProfileEditor,
|
||||
toggleProfileEditorHasError,
|
||||
|
@ -262,6 +288,31 @@ function toggleSignalConnectionsModal(): ToggleSignalConnectionsModalActionType
|
|||
};
|
||||
}
|
||||
|
||||
function showBlockingSafetyNumberChangeDialog(
|
||||
conversationsToPause: Map<string, Set<string>>,
|
||||
explodedPromise: ExplodePromiseResultType<boolean>,
|
||||
source?: SafetyNumberChangeSource
|
||||
): ThunkAction<void, RootStateType, unknown, ShowSendAnywayDialogActiontype> {
|
||||
const promiseUuid = SingleServePromise.set<boolean>(explodedPromise);
|
||||
|
||||
return dispatch => {
|
||||
dispatch({
|
||||
type: SHOW_SEND_ANYWAY_DIALOG,
|
||||
payload: {
|
||||
conversationsToPause,
|
||||
promiseUuid,
|
||||
source,
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function hideBlockingSafetyNumberChangeDialog(): HideSendAnywayDialogActiontype {
|
||||
return {
|
||||
type: HIDE_SEND_ANYWAY_DIALOG,
|
||||
};
|
||||
}
|
||||
|
||||
// Reducer
|
||||
|
||||
export function getEmptyState(): GlobalModalsStateType {
|
||||
|
@ -371,5 +422,24 @@ export function reducer(
|
|||
};
|
||||
}
|
||||
|
||||
if (action.type === SHOW_SEND_ANYWAY_DIALOG) {
|
||||
const { promiseUuid, source } = action.payload;
|
||||
|
||||
return {
|
||||
...state,
|
||||
safetyNumberChangedBlockingData: {
|
||||
promiseUuid,
|
||||
source,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === HIDE_SEND_ANYWAY_DIALOG) {
|
||||
return {
|
||||
...state,
|
||||
safetyNumberChangedBlockingData: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import {
|
|||
ConversationVerificationState,
|
||||
} from '../ducks/conversationsEnums';
|
||||
import { getOwn } from '../../util/getOwn';
|
||||
import { isNotNil } from '../../util/isNotNil';
|
||||
import type { UUIDFetchStateType } from '../../util/uuidFetchState';
|
||||
import { deconstructLookup } from '../../util/deconstructLookup';
|
||||
import type { PropsDataType as TimelinePropsType } from '../../components/conversation/Timeline';
|
||||
|
@ -1022,20 +1021,6 @@ export const getConversationIdsStoppedForVerification = createSelector(
|
|||
Object.keys(verificationDataByConversation)
|
||||
);
|
||||
|
||||
export const getConversationsStoppedForVerification = createSelector(
|
||||
getConversationByIdSelector,
|
||||
getConversationIdsStoppedForVerification,
|
||||
(
|
||||
conversationSelector: (id: string) => undefined | ConversationType,
|
||||
conversationIds: ReadonlyArray<string>
|
||||
): Array<ConversationType> => {
|
||||
const conversations = conversationIds
|
||||
.map(conversationId => conversationSelector(conversationId))
|
||||
.filter(isNotNil);
|
||||
return sortByTitle(conversations);
|
||||
}
|
||||
);
|
||||
|
||||
export const getConversationUuidsStoppingSend = createSelector(
|
||||
getConversationVerificationData,
|
||||
(pendingData): Array<string> => {
|
||||
|
|
|
@ -11,11 +11,9 @@ import { SmartCallManager } from './CallManager';
|
|||
import { SmartCustomizingPreferredReactionsModal } from './CustomizingPreferredReactionsModal';
|
||||
import { SmartGlobalModalContainer } from './GlobalModalContainer';
|
||||
import { SmartLeftPane } from './LeftPane';
|
||||
import { SmartSafetyNumberViewer } from './SafetyNumberViewer';
|
||||
import { SmartStories } from './Stories';
|
||||
import { SmartStoryViewer } from './StoryViewer';
|
||||
import type { StateType } from '../reducer';
|
||||
import { getPreferredBadgeSelector } from '../selectors/badges';
|
||||
import {
|
||||
getIntl,
|
||||
getLocaleMessages,
|
||||
|
@ -29,10 +27,8 @@ import {
|
|||
shouldShowStoriesView,
|
||||
} from '../selectors/stories';
|
||||
import { getHideMenuBar } from '../selectors/items';
|
||||
import { getConversationsStoppingSend } from '../selectors/conversations';
|
||||
import { getIsCustomizingPreferredReactions } from '../selectors/preferredReactions';
|
||||
import { mapDispatchToProps } from '../actions';
|
||||
import type { SafetyNumberProps } from '../../components/SafetyNumberChangeDialog';
|
||||
import { ErrorBoundary } from '../../components/ErrorBoundary';
|
||||
|
||||
const mapStateToProps = (state: StateType) => {
|
||||
|
@ -40,8 +36,6 @@ const mapStateToProps = (state: StateType) => {
|
|||
|
||||
return {
|
||||
...state.app,
|
||||
conversationsStoppingSend: getConversationsStoppingSend(state),
|
||||
getPreferredBadge: getPreferredBadgeSelector(state),
|
||||
i18n,
|
||||
localeMessages: getLocaleMessages(state),
|
||||
isCustomizingPreferredReactions: getIsCustomizingPreferredReactions(state),
|
||||
|
@ -56,9 +50,6 @@ const mapStateToProps = (state: StateType) => {
|
|||
),
|
||||
renderGlobalModalContainer: () => <SmartGlobalModalContainer />,
|
||||
renderLeftPane: () => <SmartLeftPane />,
|
||||
renderSafetyNumber: (props: SafetyNumberProps) => (
|
||||
<SmartSafetyNumberViewer {...props} />
|
||||
),
|
||||
isShowingStoriesView: shouldShowStoriesView(state),
|
||||
renderStories: () => (
|
||||
<ErrorBoundary>
|
||||
|
|
|
@ -3,14 +3,16 @@
|
|||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { mapDispatchToProps } from '../actions';
|
||||
import { GlobalModalContainer } from '../../components/GlobalModalContainer';
|
||||
import type { StateType } from '../reducer';
|
||||
import { GlobalModalContainer } from '../../components/GlobalModalContainer';
|
||||
import { SmartContactModal } from './ContactModal';
|
||||
import { SmartForwardMessageModal } from './ForwardMessageModal';
|
||||
import { SmartProfileEditorModal } from './ProfileEditorModal';
|
||||
import { SmartSafetyNumberModal } from './SafetyNumberModal';
|
||||
import { SmartSendAnywayDialog } from './SendAnywayDialog';
|
||||
import { SmartStoriesSettingsModal } from './StoriesSettingsModal';
|
||||
import { getConversationsStoppingSend } from '../selectors/conversations';
|
||||
import { mapDispatchToProps } from '../actions';
|
||||
|
||||
import { getIntl } from '../selectors/user';
|
||||
|
||||
|
@ -30,11 +32,16 @@ function renderStoriesSettings(): JSX.Element {
|
|||
return <SmartStoriesSettingsModal />;
|
||||
}
|
||||
|
||||
function renderSendAnywayDialog(): JSX.Element {
|
||||
return <SmartSendAnywayDialog />;
|
||||
}
|
||||
|
||||
const mapStateToProps = (state: StateType) => {
|
||||
const i18n = getIntl(state);
|
||||
|
||||
return {
|
||||
...state.globalModals,
|
||||
hasSafetyNumberChangeModal: getConversationsStoppingSend(state).length > 0,
|
||||
i18n,
|
||||
renderContactModal,
|
||||
renderForwardMessageModal,
|
||||
|
@ -45,6 +52,7 @@ const mapStateToProps = (state: StateType) => {
|
|||
contactID={String(state.globalModals.safetyNumberModalContactId)}
|
||||
/>
|
||||
),
|
||||
renderSendAnywayDialog,
|
||||
};
|
||||
};
|
||||
|
||||
|
|
75
ts/state/smart/SendAnywayDialog.tsx
Normal file
75
ts/state/smart/SendAnywayDialog.tsx
Normal file
|
@ -0,0 +1,75 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import type { LocalizerType } from '../../types/Util';
|
||||
import type { SafetyNumberChangedBlockingDataType } from '../ducks/globalModals';
|
||||
import type { StateType } from '../reducer';
|
||||
import * as SingleServePromise from '../../services/singleServePromise';
|
||||
import {
|
||||
SafetyNumberChangeDialog,
|
||||
SafetyNumberChangeSource,
|
||||
} from '../../components/SafetyNumberChangeDialog';
|
||||
import { SmartSafetyNumberViewer } from './SafetyNumberViewer';
|
||||
import { getConversationsStoppingSend } from '../selectors/conversations';
|
||||
import { getIntl, getTheme } from '../selectors/user';
|
||||
import { getPreferredBadgeSelector } from '../selectors/badges';
|
||||
import { useConversationsActions } from '../ducks/conversations';
|
||||
import { useGlobalModalActions } from '../ducks/globalModals';
|
||||
|
||||
export function SmartSendAnywayDialog(): JSX.Element {
|
||||
const { hideBlockingSafetyNumberChangeDialog } = useGlobalModalActions();
|
||||
const { cancelConversationVerification, verifyConversationsStoppingSend } =
|
||||
useConversationsActions();
|
||||
const getPreferredBadge = useSelector(getPreferredBadgeSelector);
|
||||
const i18n = useSelector<StateType, LocalizerType>(getIntl);
|
||||
const theme = useSelector(getTheme);
|
||||
|
||||
const contacts = useSelector(getConversationsStoppingSend);
|
||||
|
||||
const safetyNumberChangedBlockingData = useSelector<
|
||||
StateType,
|
||||
SafetyNumberChangedBlockingDataType | undefined
|
||||
>(state => state.globalModals.safetyNumberChangedBlockingData);
|
||||
|
||||
const explodedPromise = safetyNumberChangedBlockingData
|
||||
? SingleServePromise.get<boolean>(
|
||||
safetyNumberChangedBlockingData.promiseUuid
|
||||
)
|
||||
: undefined;
|
||||
|
||||
let confirmText: string | undefined = i18n(
|
||||
'safetyNumberChangeDialog__pending-messages'
|
||||
);
|
||||
if (safetyNumberChangedBlockingData?.source) {
|
||||
confirmText =
|
||||
safetyNumberChangedBlockingData?.source ===
|
||||
SafetyNumberChangeSource.Calling
|
||||
? i18n('callAnyway')
|
||||
: undefined;
|
||||
}
|
||||
|
||||
return (
|
||||
<SafetyNumberChangeDialog
|
||||
confirmText={confirmText}
|
||||
contacts={contacts}
|
||||
getPreferredBadge={getPreferredBadge}
|
||||
i18n={i18n}
|
||||
onCancel={() => {
|
||||
cancelConversationVerification();
|
||||
explodedPromise?.resolve(false);
|
||||
hideBlockingSafetyNumberChangeDialog();
|
||||
}}
|
||||
onConfirm={() => {
|
||||
verifyConversationsStoppingSend();
|
||||
explodedPromise?.resolve(true);
|
||||
hideBlockingSafetyNumberChangeDialog();
|
||||
}}
|
||||
renderSafetyNumber={({ contactID, onClose }) => (
|
||||
<SmartSafetyNumberViewer contactID={contactID} onClose={onClose} />
|
||||
)}
|
||||
theme={theme}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -28,11 +28,9 @@ import {
|
|||
getContactNameColorSelector,
|
||||
getConversationByIdSelector,
|
||||
getConversationUuidsStoppingSend,
|
||||
getConversationIdsStoppedForVerification,
|
||||
getConversationsByTitleSelector,
|
||||
getConversationSelector,
|
||||
getConversationsStoppingSend,
|
||||
getConversationsStoppedForVerification,
|
||||
getFilteredCandidateContactsForNewGroup,
|
||||
getFilteredComposeContacts,
|
||||
getFilteredComposeGroups,
|
||||
|
@ -333,49 +331,6 @@ describe('both/state/selectors/conversations', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('#getConversationStoppedForVerification', () => {
|
||||
it('returns an empty array if there are no conversations stopping send', () => {
|
||||
const state = getEmptyRootState();
|
||||
|
||||
assert.isEmpty(getConversationsStoppingSend(state));
|
||||
});
|
||||
|
||||
it('returns all conversations stopping send', () => {
|
||||
const convoA = makeConversation('convo a');
|
||||
const convoB = makeConversation('convo b');
|
||||
const state: StateType = {
|
||||
...getEmptyRootState(),
|
||||
conversations: {
|
||||
...getEmptyState(),
|
||||
conversationLookup: {
|
||||
'convo a': convoA,
|
||||
'convo b': convoB,
|
||||
},
|
||||
verificationDataByConversation: {
|
||||
'convo a': {
|
||||
type: ConversationVerificationState.PendingVerification as const,
|
||||
uuidsNeedingVerification: ['abc'],
|
||||
},
|
||||
'convo b': {
|
||||
type: ConversationVerificationState.PendingVerification as const,
|
||||
uuidsNeedingVerification: ['def', 'abc'],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
assert.sameDeepMembers(getConversationIdsStoppedForVerification(state), [
|
||||
'convo a',
|
||||
'convo b',
|
||||
]);
|
||||
|
||||
assert.sameDeepMembers(getConversationsStoppedForVerification(state), [
|
||||
convoA,
|
||||
convoB,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#getInvitedContactsForNewlyCreatedGroup', () => {
|
||||
it('returns an empty array if there are no invited contacts', () => {
|
||||
const state = getEmptyRootState();
|
||||
|
|
67
ts/util/blockSendUntilConversationsAreVerified.ts
Normal file
67
ts/util/blockSendUntilConversationsAreVerified.ts
Normal file
|
@ -0,0 +1,67 @@
|
|||
// Copyright 2022 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { ConversationModel } from '../models/conversations';
|
||||
import type { SafetyNumberChangeSource } from '../components/SafetyNumberChangeDialog';
|
||||
import * as log from '../logging/log';
|
||||
import { explodePromise } from './explodePromise';
|
||||
import { getConversationIdForLogging } from './idForLogging';
|
||||
|
||||
export async function blockSendUntilConversationsAreVerified(
|
||||
conversations: Array<ConversationModel>,
|
||||
source?: SafetyNumberChangeSource
|
||||
): Promise<boolean> {
|
||||
const conversationsToPause = new Map<string, Set<string>>();
|
||||
|
||||
await Promise.all(
|
||||
conversations.map(async conversation => {
|
||||
if (!conversation) {
|
||||
return;
|
||||
}
|
||||
|
||||
const uuidsStoppingSend = new Set<string>();
|
||||
|
||||
await conversation.updateVerified();
|
||||
const unverifieds = conversation.getUnverified();
|
||||
|
||||
if (unverifieds.length) {
|
||||
unverifieds.forEach(unverifiedConversation => {
|
||||
const uuid = unverifiedConversation.get('uuid');
|
||||
if (uuid) {
|
||||
uuidsStoppingSend.add(uuid);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const untrusted = conversation.getUntrusted();
|
||||
if (untrusted.length) {
|
||||
untrusted.forEach(untrustedConversation => {
|
||||
const uuid = untrustedConversation.get('uuid');
|
||||
if (uuid) {
|
||||
uuidsStoppingSend.add(uuid);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (uuidsStoppingSend.size) {
|
||||
log.info('blockSendUntilConversationsAreVerified: blocking send', {
|
||||
id: getConversationIdForLogging(conversation.attributes),
|
||||
untrustedCount: uuidsStoppingSend.size,
|
||||
});
|
||||
conversationsToPause.set(conversation.id, uuidsStoppingSend);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
if (conversationsToPause.size) {
|
||||
const explodedPromise = explodePromise<boolean>();
|
||||
window.reduxActions.globalModals.showBlockingSafetyNumberChangeDialog(
|
||||
conversationsToPause,
|
||||
explodedPromise,
|
||||
source
|
||||
);
|
||||
return explodedPromise.promise;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { ConversationModel } from '../models/conversations';
|
||||
|
||||
export async function markAllAsApproved(
|
||||
untrusted: ReadonlyArray<ConversationModel>
|
||||
): Promise<void> {
|
||||
await Promise.all(untrusted.map(contact => contact.setApproved()));
|
||||
}
|
|
@ -1,18 +0,0 @@
|
|||
// Copyright 2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { ConversationModel } from '../models/conversations';
|
||||
|
||||
export async function markAllAsVerifiedDefault(
|
||||
unverified: ReadonlyArray<ConversationModel>
|
||||
): Promise<void> {
|
||||
await Promise.all(
|
||||
unverified.map(contact => {
|
||||
if (contact.isUnverified()) {
|
||||
return contact.setVerifiedDefault();
|
||||
}
|
||||
|
||||
return null;
|
||||
})
|
||||
);
|
||||
}
|
|
@ -2,15 +2,14 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import type { AttachmentType } from '../types/Attachment';
|
||||
import type { ConversationModel } from '../models/conversations';
|
||||
import type { LinkPreviewType } from '../types/message/LinkPreviews';
|
||||
import type { MessageAttributesType } from '../model-types.d';
|
||||
import * as log from '../logging/log';
|
||||
import { SafetyNumberChangeSource } from '../components/SafetyNumberChangeDialog';
|
||||
import { blockSendUntilConversationsAreVerified } from './blockSendUntilConversationsAreVerified';
|
||||
import { getMessageIdForLogging } from './idForLogging';
|
||||
import { markAllAsApproved } from './markAllAsApproved';
|
||||
import { markAllAsVerifiedDefault } from './markAllAsVerifiedDefault';
|
||||
import { isNotNil } from './isNotNil';
|
||||
import { resetLinkPreview } from '../services/LinkPreview';
|
||||
import { showSafetyNumberChangeDialog } from '../shims/showSafetyNumberChangeDialog';
|
||||
|
||||
export async function maybeForwardMessage(
|
||||
messageAttributes: MessageAttributesType,
|
||||
|
@ -29,9 +28,9 @@ export async function maybeForwardMessage(
|
|||
});
|
||||
}
|
||||
|
||||
const conversations = conversationIds.map(id =>
|
||||
window.ConversationController.get(id)
|
||||
);
|
||||
const conversations = conversationIds
|
||||
.map(id => window.ConversationController.get(id))
|
||||
.filter(isNotNil);
|
||||
|
||||
const cannotSend = conversations.some(
|
||||
conversation =>
|
||||
|
@ -42,69 +41,16 @@ export async function maybeForwardMessage(
|
|||
}
|
||||
|
||||
// Verify that all contacts that we're forwarding
|
||||
// to are verified and trusted
|
||||
const unverifiedContacts: Array<ConversationModel> = [];
|
||||
const untrustedContacts: Array<ConversationModel> = [];
|
||||
await Promise.all(
|
||||
conversations.map(async conversation => {
|
||||
if (conversation) {
|
||||
await conversation.updateVerified();
|
||||
const unverifieds = conversation.getUnverified();
|
||||
if (unverifieds.length) {
|
||||
unverifieds.forEach(unverifiedConversation =>
|
||||
unverifiedContacts.push(unverifiedConversation)
|
||||
);
|
||||
}
|
||||
|
||||
const untrusted = conversation.getUntrusted();
|
||||
if (untrusted.length) {
|
||||
untrusted.forEach(untrustedConversation =>
|
||||
untrustedContacts.push(untrustedConversation)
|
||||
);
|
||||
}
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// to are verified and trusted.
|
||||
// If there are any unverified or untrusted contacts, show the
|
||||
// SendAnywayDialog and if we're fine with sending then mark all as
|
||||
// verified and trusted and continue the send.
|
||||
const iffyConversations = [...unverifiedContacts, ...untrustedContacts];
|
||||
if (iffyConversations.length) {
|
||||
const forwardMessageModal = document.querySelector<HTMLElement>(
|
||||
'.module-ForwardMessageModal'
|
||||
);
|
||||
if (forwardMessageModal) {
|
||||
forwardMessageModal.style.display = 'none';
|
||||
}
|
||||
const sendAnyway = await new Promise(resolve => {
|
||||
showSafetyNumberChangeDialog({
|
||||
contacts: iffyConversations,
|
||||
reject: () => {
|
||||
resolve(false);
|
||||
},
|
||||
resolve: () => {
|
||||
resolve(true);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
if (!sendAnyway) {
|
||||
if (forwardMessageModal) {
|
||||
forwardMessageModal.style.display = 'block';
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
let verifyPromise: Promise<void> | undefined;
|
||||
let approvePromise: Promise<void> | undefined;
|
||||
if (unverifiedContacts.length) {
|
||||
verifyPromise = markAllAsVerifiedDefault(unverifiedContacts);
|
||||
}
|
||||
if (untrustedContacts.length) {
|
||||
approvePromise = markAllAsApproved(untrustedContacts);
|
||||
}
|
||||
await Promise.all([verifyPromise, approvePromise]);
|
||||
const canSend = await blockSendUntilConversationsAreVerified(
|
||||
conversations,
|
||||
SafetyNumberChangeSource.MessageSend
|
||||
);
|
||||
if (!canSend) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const sendMessageOptions = { dontClearDraft: true };
|
||||
|
|
|
@ -51,7 +51,6 @@ import { getTheme } from '../state/selectors/user';
|
|||
import { ReactWrapperView } from './ReactWrapperView';
|
||||
import type { Lightbox } from '../components/Lightbox';
|
||||
import { ConversationDetailsMembershipList } from '../components/conversation/conversation-details/ConversationDetailsMembershipList';
|
||||
import { showSafetyNumberChangeDialog } from '../shims/showSafetyNumberChangeDialog';
|
||||
import * as log from '../logging/log';
|
||||
import type { EmbeddedContactType } from '../types/EmbeddedContact';
|
||||
import { createConversationView } from '../state/roots/createConversationView';
|
||||
|
@ -84,8 +83,6 @@ import { ToastTapToViewExpiredOutgoing } from '../components/ToastTapToViewExpir
|
|||
import { ToastUnableToLoadAttachment } from '../components/ToastUnableToLoadAttachment';
|
||||
import { ToastCannotOpenGiftBadge } from '../components/ToastCannotOpenGiftBadge';
|
||||
import { deleteDraftAttachment } from '../util/deleteDraftAttachment';
|
||||
import { markAllAsApproved } from '../util/markAllAsApproved';
|
||||
import { markAllAsVerifiedDefault } from '../util/markAllAsVerifiedDefault';
|
||||
import { retryMessageSend } from '../util/retryMessageSend';
|
||||
import { isNotNil } from '../util/isNotNil';
|
||||
import { markViewed } from '../services/MessageUpdater';
|
||||
|
@ -114,6 +111,8 @@ import { closeLightbox, showLightbox } from '../util/showLightbox';
|
|||
import { saveAttachment } from '../util/saveAttachment';
|
||||
import { sendDeleteForEveryoneMessage } from '../util/sendDeleteForEveryoneMessage';
|
||||
import { SECOND } from '../util/durations';
|
||||
import { blockSendUntilConversationsAreVerified } from '../util/blockSendUntilConversationsAreVerified';
|
||||
import { SafetyNumberChangeSource } from '../components/SafetyNumberChangeDialog';
|
||||
|
||||
type AttachmentOptions = {
|
||||
messageId: string;
|
||||
|
@ -2321,57 +2320,33 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
}
|
||||
|
||||
async isCallSafe(): Promise<boolean> {
|
||||
const contacts = await this.getUntrustedContacts();
|
||||
if (contacts.length) {
|
||||
const callAnyway = await this.showSendAnywayDialog(
|
||||
contacts,
|
||||
window.i18n('callAnyway')
|
||||
const callAnyway = await blockSendUntilConversationsAreVerified(
|
||||
[this.model],
|
||||
SafetyNumberChangeSource.Calling
|
||||
);
|
||||
|
||||
if (!callAnyway) {
|
||||
log.info(
|
||||
'Safety number change dialog not accepted, new call not allowed.'
|
||||
);
|
||||
if (!callAnyway) {
|
||||
log.info(
|
||||
'Safety number change dialog not accepted, new call not allowed.'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
showSendAnywayDialog(
|
||||
contacts: Array<ConversationModel>,
|
||||
confirmText?: string
|
||||
): Promise<boolean> {
|
||||
return new Promise(resolve => {
|
||||
showSafetyNumberChangeDialog({
|
||||
confirmText,
|
||||
contacts,
|
||||
reject: () => {
|
||||
resolve(false);
|
||||
},
|
||||
resolve: () => {
|
||||
resolve(true);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async sendStickerMessage(options: {
|
||||
packId: string;
|
||||
stickerId: number;
|
||||
force?: boolean;
|
||||
}): Promise<void> {
|
||||
const { model }: { model: ConversationModel } = this;
|
||||
|
||||
try {
|
||||
const contacts = await this.getUntrustedContacts(options);
|
||||
|
||||
if (contacts.length) {
|
||||
const sendAnyway = await this.showSendAnywayDialog(contacts);
|
||||
if (sendAnyway) {
|
||||
this.sendStickerMessage({ ...options, force: true });
|
||||
}
|
||||
|
||||
const sendAnyway = await blockSendUntilConversationsAreVerified(
|
||||
[this.model],
|
||||
SafetyNumberChangeSource.MessageSend
|
||||
);
|
||||
if (!sendAnyway) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -2386,40 +2361,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
}
|
||||
}
|
||||
|
||||
async getUntrustedContacts(
|
||||
options: { force?: boolean } = {}
|
||||
): Promise<Array<ConversationModel>> {
|
||||
const { model }: { model: ConversationModel } = this;
|
||||
|
||||
// This will go to the trust store for the latest identity key information,
|
||||
// and may result in the display of a new banner for this conversation.
|
||||
await model.updateVerified();
|
||||
const unverifiedContacts = model.getUnverified();
|
||||
|
||||
if (options.force) {
|
||||
if (unverifiedContacts.length) {
|
||||
await markAllAsVerifiedDefault(unverifiedContacts);
|
||||
// We only want force to break us through one layer of checks
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
options.force = false;
|
||||
}
|
||||
} else if (unverifiedContacts.length) {
|
||||
return unverifiedContacts;
|
||||
}
|
||||
|
||||
const untrustedContacts = model.getUntrusted();
|
||||
|
||||
if (options.force) {
|
||||
if (untrustedContacts.length) {
|
||||
await markAllAsApproved(untrustedContacts);
|
||||
}
|
||||
} else if (untrustedContacts.length) {
|
||||
return untrustedContacts;
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
async setQuoteMessage(messageId: null | string): Promise<void> {
|
||||
const { model } = this;
|
||||
const message = messageId ? await getMessageById(messageId) : undefined;
|
||||
|
@ -2546,7 +2487,6 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
mentions: BodyRangesType = [],
|
||||
options: {
|
||||
draftAttachments?: ReadonlyArray<AttachmentType>;
|
||||
force?: boolean;
|
||||
timestamp?: number;
|
||||
voiceNoteAttachment?: AttachmentType;
|
||||
} = {}
|
||||
|
@ -2558,15 +2498,12 @@ export class ConversationView extends window.Backbone.View<ConversationModel> {
|
|||
|
||||
try {
|
||||
this.disableMessageField();
|
||||
const contacts = await this.getUntrustedContacts(options);
|
||||
|
||||
if (contacts.length) {
|
||||
const sendAnyway = await this.showSendAnywayDialog(contacts);
|
||||
if (sendAnyway) {
|
||||
this.sendMessage(message, mentions, { force: true, timestamp });
|
||||
return;
|
||||
}
|
||||
|
||||
const sendAnyway = await blockSendUntilConversationsAreVerified(
|
||||
[this.model],
|
||||
SafetyNumberChangeSource.MessageSend
|
||||
);
|
||||
if (!sendAnyway) {
|
||||
this.enableMessageField();
|
||||
return;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue