2022-03-24 21:46:17 +00:00
|
|
|
// Copyright 2022 Signal Messenger, LLC
|
|
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
|
2024-02-06 02:13:13 +00:00
|
|
|
import React, { useCallback } from 'react';
|
2022-03-24 21:46:17 +00:00
|
|
|
import { useSelector } from 'react-redux';
|
2024-02-06 02:13:13 +00:00
|
|
|
import { mapValues } from 'lodash';
|
2022-03-24 21:46:17 +00:00
|
|
|
import type { StateType } from '../reducer';
|
|
|
|
|
|
|
|
import { ContactSpoofingReviewDialog } from '../../components/conversation/ContactSpoofingReviewDialog';
|
|
|
|
|
2022-12-21 03:25:10 +00:00
|
|
|
import { useConversationsActions } from '../ducks/conversations';
|
2022-03-24 21:46:17 +00:00
|
|
|
import type { GetConversationByIdType } from '../selectors/conversations';
|
2024-02-06 02:13:13 +00:00
|
|
|
import {
|
|
|
|
getConversationSelector,
|
|
|
|
getConversationByServiceIdSelector,
|
|
|
|
getSafeConversationWithSameTitle,
|
|
|
|
} from '../selectors/conversations';
|
|
|
|
import { getOwn } from '../../util/getOwn';
|
|
|
|
import { assertDev } from '../../util/assert';
|
2022-03-24 21:46:17 +00:00
|
|
|
import { ContactSpoofingType } from '../../util/contactSpoofing';
|
2024-02-06 02:13:13 +00:00
|
|
|
import { getGroupMemberships } from '../../util/getGroupMemberships';
|
|
|
|
import { isSignalConnection } from '../../util/getSignalConnections';
|
|
|
|
import {
|
|
|
|
getCollisionsFromMemberships,
|
|
|
|
invertIdsByTitle,
|
|
|
|
} from '../../util/groupMemberNameCollisions';
|
2022-12-21 03:25:10 +00:00
|
|
|
import { useGlobalModalActions } from '../ducks/globalModals';
|
|
|
|
import { getPreferredBadgeSelector } from '../selectors/badges';
|
|
|
|
import { getIntl, getTheme } from '../selectors/user';
|
2022-03-24 21:46:17 +00:00
|
|
|
|
2024-02-06 02:13:13 +00:00
|
|
|
export type PropsType = Readonly<{
|
|
|
|
conversationId: string;
|
|
|
|
onClose: () => void;
|
|
|
|
}>;
|
2022-03-24 21:46:17 +00:00
|
|
|
|
2022-11-18 00:45:19 +00:00
|
|
|
export function SmartContactSpoofingReviewDialog(
|
|
|
|
props: PropsType
|
2024-02-06 02:13:13 +00:00
|
|
|
): JSX.Element | null {
|
|
|
|
const { conversationId } = props;
|
2022-03-24 21:46:17 +00:00
|
|
|
|
|
|
|
const getConversation = useSelector<StateType, GetConversationByIdType>(
|
|
|
|
getConversationSelector
|
|
|
|
);
|
|
|
|
|
2022-12-21 03:25:10 +00:00
|
|
|
const {
|
|
|
|
acceptConversation,
|
|
|
|
blockAndReportSpam,
|
|
|
|
blockConversation,
|
|
|
|
deleteConversation,
|
|
|
|
removeMember,
|
2024-02-07 21:33:34 +00:00
|
|
|
updateSharedGroups,
|
2022-12-21 03:25:10 +00:00
|
|
|
} = useConversationsActions();
|
2024-02-06 02:13:13 +00:00
|
|
|
const { showContactModal, toggleSignalConnectionsModal } =
|
|
|
|
useGlobalModalActions();
|
2022-12-21 03:25:10 +00:00
|
|
|
const getPreferredBadge = useSelector(getPreferredBadgeSelector);
|
|
|
|
const i18n = useSelector(getIntl);
|
|
|
|
const theme = useSelector(getTheme);
|
2024-02-06 02:13:13 +00:00
|
|
|
const getConversationByServiceId = useSelector(
|
|
|
|
getConversationByServiceIdSelector
|
|
|
|
);
|
|
|
|
const conversation = getConversation(conversationId);
|
|
|
|
|
|
|
|
// Just binding the options argument
|
|
|
|
const safeConversationSelector = useCallback(
|
|
|
|
(state: StateType) => {
|
|
|
|
return getSafeConversationWithSameTitle(state, {
|
|
|
|
possiblyUnsafeConversation: conversation,
|
|
|
|
});
|
|
|
|
},
|
|
|
|
[conversation]
|
|
|
|
);
|
|
|
|
const safeConvo = useSelector(safeConversationSelector);
|
2022-12-21 03:25:10 +00:00
|
|
|
|
|
|
|
const sharedProps = {
|
2024-02-06 02:13:13 +00:00
|
|
|
...props,
|
2022-12-21 03:25:10 +00:00
|
|
|
acceptConversation,
|
|
|
|
blockAndReportSpam,
|
|
|
|
blockConversation,
|
|
|
|
deleteConversation,
|
|
|
|
getPreferredBadge,
|
|
|
|
i18n,
|
|
|
|
removeMember,
|
2024-02-07 21:33:34 +00:00
|
|
|
updateSharedGroups,
|
2022-12-21 03:25:10 +00:00
|
|
|
showContactModal,
|
2024-02-06 02:13:13 +00:00
|
|
|
toggleSignalConnectionsModal,
|
2022-12-21 03:25:10 +00:00
|
|
|
theme,
|
|
|
|
};
|
|
|
|
|
2024-02-06 02:13:13 +00:00
|
|
|
if (conversation.type === 'group') {
|
|
|
|
const { memberships } = getGroupMemberships(
|
|
|
|
conversation,
|
|
|
|
getConversationByServiceId
|
|
|
|
);
|
|
|
|
const groupNameCollisions = getCollisionsFromMemberships(memberships);
|
|
|
|
|
|
|
|
const previouslyAcknowledgedTitlesById = invertIdsByTitle(
|
|
|
|
conversation.acknowledgedGroupNameCollisions
|
|
|
|
);
|
|
|
|
|
|
|
|
const collisionInfoByTitle = mapValues(groupNameCollisions, collisions =>
|
|
|
|
collisions.map(collision => ({
|
|
|
|
conversation: collision,
|
|
|
|
isSignalConnection: isSignalConnection(collision),
|
|
|
|
oldName: getOwn(previouslyAcknowledgedTitlesById, collision.id),
|
|
|
|
}))
|
|
|
|
);
|
|
|
|
|
2022-03-24 21:46:17 +00:00
|
|
|
return (
|
|
|
|
<ContactSpoofingReviewDialog
|
2022-12-21 03:25:10 +00:00
|
|
|
{...sharedProps}
|
2024-02-06 02:13:13 +00:00
|
|
|
type={ContactSpoofingType.MultipleGroupMembersWithSameTitle}
|
|
|
|
group={conversation}
|
|
|
|
collisionInfoByTitle={collisionInfoByTitle}
|
2022-03-24 21:46:17 +00:00
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2024-02-06 02:13:13 +00:00
|
|
|
const possiblyUnsafeConvo = conversation;
|
|
|
|
assertDev(
|
|
|
|
possiblyUnsafeConvo.type === 'direct',
|
|
|
|
'DirectConversationWithSameTitle: expects possibly unsafe direct ' +
|
|
|
|
'conversation'
|
|
|
|
);
|
|
|
|
|
|
|
|
if (!safeConvo) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
const possiblyUnsafe = {
|
|
|
|
conversation: possiblyUnsafeConvo,
|
|
|
|
isSignalConnection: isSignalConnection(possiblyUnsafeConvo),
|
|
|
|
};
|
|
|
|
const safe = {
|
|
|
|
conversation: safeConvo,
|
|
|
|
isSignalConnection: isSignalConnection(safeConvo),
|
|
|
|
};
|
|
|
|
|
|
|
|
return (
|
|
|
|
<ContactSpoofingReviewDialog
|
|
|
|
{...sharedProps}
|
|
|
|
type={ContactSpoofingType.DirectConversationWithSameTitle}
|
|
|
|
possiblyUnsafe={possiblyUnsafe}
|
|
|
|
safe={safe}
|
|
|
|
/>
|
|
|
|
);
|
2022-11-18 00:45:19 +00:00
|
|
|
}
|