Update contact spoofing banner & dialog

This commit is contained in:
trevor-signal 2023-05-09 11:33:39 -04:00 committed by GitHub
parent 4b7f1dbada
commit 7b039fa526
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 178 additions and 126 deletions

View file

@ -9455,9 +9455,13 @@
"description": "(deleted 04/04/2023) Shown in the timeline warning when you multiple group members have the same name" "description": "(deleted 04/04/2023) Shown in the timeline warning when you multiple group members have the same name"
}, },
"icu:ContactSpoofing__same-name-in-group--link": { "icu:ContactSpoofing__same-name-in-group--link": {
"messageformat": "{count, plural, one {# group member has} other {# group members have}} the same name. <reviewRequestLink>Review request</reviewRequestLink>", "messageformat": "{count, plural, one {# group member has} other {# group members have}} the same name. <reviewRequestLink>Review members</reviewRequestLink>",
"description": "Shown in the timeline warning when you multiple group members have the same name" "description": "Shown in the timeline warning when you multiple group members have the same name"
}, },
"icu:ContactSpoofing__same-names-in-group--link": {
"messageformat": "{count, plural, one {# name conflict was} other {# name conflicts were}} found in this group. <reviewRequestLink>Review members</reviewRequestLink>",
"description": "Shown in the timeline warning when multiple names are shared by members of a group."
},
"ContactSpoofing__same-name__link": { "ContactSpoofing__same-name__link": {
"message": "Review request", "message": "Review request",
"description": "(deleted 03/29/2023) Shown in the timeline warning when you have a message request from someone with the same name as someone else" "description": "(deleted 03/29/2023) Shown in the timeline warning when you have a message request from someone with the same name as someone else"
@ -9472,7 +9476,7 @@
}, },
"icu:ContactSpoofing__same-name-in-group__link": { "icu:ContactSpoofing__same-name-in-group__link": {
"messageformat": "Click to review", "messageformat": "Click to review",
"description": "Shown in the timeline warning when you multiple group members have the same name" "description": "(deleted 05/04/2023) Shown in the timeline warning when you multiple group members have the same name"
}, },
"ContactSpoofingReviewDialog__title": { "ContactSpoofingReviewDialog__title": {
"message": "Review request", "message": "Review request",
@ -9522,6 +9526,10 @@
"messageformat": "{count, plural, one {# group member} other {# group members}} have similar names. Review the members below or choose to take action.", "messageformat": "{count, plural, one {# group member} other {# group members}} have similar names. Review the members below or choose to take action.",
"description": "Description for the group contact spoofing review dialog" "description": "Description for the group contact spoofing review dialog"
}, },
"icu:ContactSpoofingReviewDialog__group__multiple-conflicts__description": {
"messageformat": "{count, plural, one {# name conflict was} other {# name conflicts were}} found in this group. Review the members below or choose to take action.",
"description": "Description for the group contact spoofing review dialog when there are multiple shared names"
},
"ContactSpoofingReviewDialog__group__members-header": { "ContactSpoofingReviewDialog__group__members-header": {
"message": "Members", "message": "Members",
"description": "(deleted 03/29/2023) Header in the group contact spoofing review dialog. After this header, there will be a list of members" "description": "(deleted 03/29/2023) Header in the group contact spoofing review dialog. After this header, there will be a list of members"

View file

@ -16,14 +16,20 @@
} }
} }
&__description {
margin-top: 4px;
}
h2 { h2 {
@include font-body-1-bold; @include font-body-1-bold;
margin-top: 28px;
margin-bottom: 20px;
} }
hr { hr {
border: 0; border: 0;
height: 1px; height: 1px;
margin-block: 16px; margin-block: 20px;
margin-inline: 0; margin-inline: 0;
@include light-theme { @include light-theme {
@ -35,7 +41,7 @@
} }
&__buttons { &__buttons {
margin-top: 8px; margin-top: 12px;
.module-Button:not(:last-child) { .module-Button:not(:last-child) {
margin-inline-end: 12px; margin-inline-end: 12px;

View file

@ -12,7 +12,7 @@
margin-inline-start: 12px; margin-inline-start: 12px;
&__contact-name { &__contact-name {
@include font-body-1; @include font-body-1-bold;
} }
&__property { &__property {

View file

@ -3,7 +3,6 @@
import type { ReactChild, ReactNode } from 'react'; import type { ReactChild, ReactNode } from 'react';
import React, { useState } from 'react'; import React, { useState } from 'react';
import { concat, orderBy } from 'lodash';
import type { LocalizerType, ThemeType } from '../../types/Util'; import type { LocalizerType, ThemeType } from '../../types/Util';
import type { ConversationType } from '../../state/ducks/conversations'; import type { ConversationType } from '../../state/ducks/conversations';
@ -235,31 +234,40 @@ export function ContactSpoofingReviewDialog(props: PropsType): JSX.Element {
} }
case ContactSpoofingType.MultipleGroupMembersWithSameTitle: { case ContactSpoofingType.MultipleGroupMembersWithSameTitle: {
const { group, collisionInfoByTitle } = props; const { group, collisionInfoByTitle } = props;
const sharedTitles = Object.keys(collisionInfoByTitle);
const unsortedConversationInfos = concat( const numSharedTitles = sharedTitles.length;
// This empty array exists to appease Lodash's type definitions. const totalConversations = Object.values(collisionInfoByTitle).reduce(
[], (sum, conversationInfos) => sum + conversationInfos.length,
...Object.values(collisionInfoByTitle) 0
); );
const conversationInfos = orderBy(unsortedConversationInfos, [
// We normally use an `Intl.Collator` to sort by title. We do this instead, as
// we only really care about stability (not perfect ordering).
'title',
'id',
]);
title = i18n('icu:ContactSpoofingReviewDialog__group__title'); title = i18n('icu:ContactSpoofingReviewDialog__group__title');
contents = ( contents = (
<> <>
<p> <p className="module-ContactSpoofingReviewDialog__description">
{i18n('icu:ContactSpoofingReviewDialog__group__description', { {numSharedTitles > 1
count: conversationInfos.length, ? i18n(
'icu:ContactSpoofingReviewDialog__group__multiple-conflicts__description',
{
count: numSharedTitles,
}
)
: i18n('icu:ContactSpoofingReviewDialog__group__description', {
count: totalConversations,
})} })}
</p> </p>
{Object.values(collisionInfoByTitle).map(
(conversationInfos, titleIdx) => {
return (
<>
<h2> <h2>
{i18n('icu:ContactSpoofingReviewDialog__group__members-header')} {i18n(
'icu:ContactSpoofingReviewDialog__group__members-header'
)}
</h2> </h2>
{conversationInfos.map((conversationInfo, index) => { {conversationInfos.map(
(conversationInfo, conversationIdx) => {
let button: ReactNode; let button: ReactNode;
if (group.areWeAdmin) { if (group.areWeAdmin) {
button = ( button = (
@ -268,12 +276,15 @@ export function ContactSpoofingReviewDialog(props: PropsType): JSX.Element {
onClick={() => { onClick={() => {
setConfirmationState({ setConfirmationState({
type: ConfirmationStateType.ConfirmingGroupRemoval, type: ConfirmationStateType.ConfirmingGroupRemoval,
affectedConversation: conversationInfo.conversation, affectedConversation:
conversationInfo.conversation,
group, group,
}); });
}} }}
> >
{i18n('icu:RemoveGroupMemberConfirmation__remove-button')} {i18n(
'icu:RemoveGroupMemberConfirmation__remove-button'
)}
</Button> </Button>
); );
} else if (conversationInfo.conversation.isBlocked) { } else if (conversationInfo.conversation.isBlocked) {
@ -281,20 +292,25 @@ export function ContactSpoofingReviewDialog(props: PropsType): JSX.Element {
<Button <Button
variant={ButtonVariant.SecondaryAffirmative} variant={ButtonVariant.SecondaryAffirmative}
onClick={() => { onClick={() => {
acceptConversation(conversationInfo.conversation.id); acceptConversation(
conversationInfo.conversation.id
);
}} }}
> >
{i18n('icu:MessageRequests--unblock')} {i18n('icu:MessageRequests--unblock')}
</Button> </Button>
); );
} else if (!isInSystemContacts(conversationInfo.conversation)) { } else if (
!isInSystemContacts(conversationInfo.conversation)
) {
button = ( button = (
<Button <Button
variant={ButtonVariant.SecondaryDestructive} variant={ButtonVariant.SecondaryDestructive}
onClick={() => { onClick={() => {
setConfirmationState({ setConfirmationState({
type: ConfirmationStateType.ConfirmingBlock, type: ConfirmationStateType.ConfirmingBlock,
affectedConversation: conversationInfo.conversation, affectedConversation:
conversationInfo.conversation,
}); });
}} }}
> >
@ -326,7 +342,6 @@ export function ContactSpoofingReviewDialog(props: PropsType): JSX.Element {
return ( return (
<> <>
{index !== 0 && <hr />}
<ContactSpoofingReviewDialogPerson <ContactSpoofingReviewDialogPerson
key={conversationInfo.conversation.id} key={conversationInfo.conversation.id}
conversation={conversationInfo.conversation} conversation={conversationInfo.conversation}
@ -341,9 +356,18 @@ export function ContactSpoofingReviewDialog(props: PropsType): JSX.Element {
</div> </div>
)} )}
</ContactSpoofingReviewDialogPerson> </ContactSpoofingReviewDialogPerson>
{titleIdx < sharedTitles.length - 1 ||
conversationIdx < conversationInfos.length - 1 ? (
<hr />
) : null}
</> </>
); );
})} }
)}
</>
);
}
)}
</> </>
); );
break; break;

View file

@ -19,6 +19,7 @@ import { clearTimeoutIfNecessary } from '../../util/clearTimeoutIfNecessary';
import { WidthBreakpoint } from '../_util'; import { WidthBreakpoint } from '../_util';
import { ErrorBoundary } from './ErrorBoundary'; import { ErrorBoundary } from './ErrorBoundary';
import type { FullJSXType } from '../Intl';
import { Intl } from '../Intl'; import { Intl } from '../Intl';
import { TimelineWarning } from './TimelineWarning'; import { TimelineWarning } from './TimelineWarning';
import { TimelineWarnings } from './TimelineWarnings'; import { TimelineWarnings } from './TimelineWarnings';
@ -941,6 +942,17 @@ export class Timeline extends React.Component<
break; break;
case ContactSpoofingType.MultipleGroupMembersWithSameTitle: { case ContactSpoofingType.MultipleGroupMembersWithSameTitle: {
const { groupNameCollisions } = warning; const { groupNameCollisions } = warning;
const numberOfSharedNames = Object.keys(groupNameCollisions).length;
const reviewRequestLink: FullJSXType = parts => (
<TimelineWarning.Link
onClick={() => {
reviewGroupMemberNameCollision(id);
}}
>
{parts}
</TimelineWarning.Link>
);
if (numberOfSharedNames === 1) {
text = ( text = (
<Intl <Intl
i18n={i18n} i18n={i18n}
@ -950,20 +962,22 @@ export class Timeline extends React.Component<
(result, conversations) => result + conversations.length, (result, conversations) => result + conversations.length,
0 0
), ),
// This is a render props, not a component reviewRequestLink,
// eslint-disable-next-line react/no-unstable-nested-components
reviewRequestLink: parts => (
<TimelineWarning.Link
onClick={() => {
reviewGroupMemberNameCollision(id);
}}
>
{parts}
</TimelineWarning.Link>
),
}} }}
/> />
); );
} else {
text = (
<Intl
i18n={i18n}
id="icu:ContactSpoofing__same-names-in-group--link"
components={{
count: numberOfSharedNames,
reviewRequestLink,
}}
/>
);
}
onClose = () => { onClose = () => {
acknowledgeGroupMemberNameCollisions(id, groupNameCollisions); acknowledgeGroupMemberNameCollisions(id, groupNameCollisions);
}; };