Move getUntrustedContacts out of conversation_view
This commit is contained in:
parent
96c4cc4bcf
commit
936ce91b2e
19 changed files with 410 additions and 414 deletions
|
@ -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}
|
||||
/>
|
||||
);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue