Move getUntrustedContacts out of conversation_view

This commit is contained in:
Josh Perez 2022-08-16 19:59:11 -04:00 committed by GitHub
parent 96c4cc4bcf
commit 936ce91b2e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 410 additions and 414 deletions

View file

@ -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];

View file

@ -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;
}

View file

@ -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> => {

View file

@ -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>

View file

@ -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,
};
};

View 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}
/>
);
}