Show common groups in contact modal

This commit is contained in:
Evan Hahn 2021-04-16 10:51:26 -05:00 committed by Scott Nonnenberg
parent 8ac2d8fcec
commit 885ff5fe42
6 changed files with 121 additions and 88 deletions

View file

@ -3064,8 +3064,8 @@
} }
} }
}, },
"ConversationHero--membership-1": { "member-of-1-group": {
"message": "Member of $group$.", "message": "Member of $group$",
"description": "Shown in the conversation hero to indicate this user is a member of a mutual group", "description": "Shown in the conversation hero to indicate this user is a member of a mutual group",
"placeholders": { "placeholders": {
"group": { "group": {
@ -3074,8 +3074,8 @@
} }
} }
}, },
"ConversationHero--membership-2": { "member-of-2-groups": {
"message": "Member of $group1$ and $group2$.", "message": "Member of $group1$ and $group2$",
"description": "Shown in the conversation hero to indicate this user is a member of at least two mutual groups", "description": "Shown in the conversation hero to indicate this user is a member of at least two mutual groups",
"placeholders": { "placeholders": {
"group1": { "group1": {
@ -3088,8 +3088,8 @@
} }
} }
}, },
"ConversationHero--membership-3": { "member-of-3-groups": {
"message": "Member of $group1$, $group2$, and $group3$.", "message": "Member of $group1$, $group2$, and $group3$",
"description": "Shown in the conversation hero to indicate this user is a member of at least three mutual groups", "description": "Shown in the conversation hero to indicate this user is a member of at least three mutual groups",
"placeholders": { "placeholders": {
"group1": { "group1": {
@ -3106,8 +3106,8 @@
} }
} }
}, },
"ConversationHero--membership-extra": { "member-of-more-than-3-groups": {
"message": "Member of $group1$, $group2$, $group3$ and $remainingCount$ more.", "message": "Member of $group1$, $group2$, $group3$ and $remainingCount$ more",
"description": "Shown in the conversation hero to indicate this user is a member of at least three mutual groups", "description": "Shown in the conversation hero to indicate this user is a member of at least three mutual groups",
"placeholders": { "placeholders": {
"group1": { "group1": {
@ -3139,7 +3139,7 @@
} }
}, },
"no-groups-in-common": { "no-groups-in-common": {
"message": "No groups in common.", "message": "No groups in common",
"description": "Shown to indicate this user is not a member of any groups" "description": "Shown to indicate this user is not a member of any groups"
}, },
"acceptCall": { "acceptCall": {

View file

@ -2115,7 +2115,6 @@ $timer-icons: '55', '50', '45', '40', '35', '30', '25', '20', '15', '10', '05',
.module-about { .module-about {
&__container { &__container {
margin-bottom: 8px;
margin-left: auto; margin-left: auto;
margin-right: auto; margin-right: auto;
max-width: 248px; max-width: 248px;
@ -10484,9 +10483,14 @@ $contact-modal-padding: 18px;
margin-top: 6px; margin-top: 6px;
} }
.module-contact-modal__profile-and-number { .module-contact-modal__info {
color: $color-gray-60; text-align: center;
max-width: 248px;
margin-top: 8px;
@include light-theme {
color: $color-gray-60;
}
@include dark-theme { @include dark-theme {
color: $color-gray-25; color: $color-gray-25;
} }

View file

@ -0,0 +1,83 @@
// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import React, { FunctionComponent } from 'react';
import { take } from 'lodash';
import { Emojify } from './conversation/Emojify';
import { Intl } from './Intl';
import { LocalizerType } from '../types/Util';
type PropsType = {
i18n: LocalizerType;
nameClassName?: string;
sharedGroupNames: Array<string>;
};
export const SharedGroupNames: FunctionComponent<PropsType> = ({
i18n,
nameClassName,
sharedGroupNames,
}) => {
const firstThreeGroups = take(sharedGroupNames, 3).map((group, i) => (
// We cannot guarantee uniqueness of group names
// eslint-disable-next-line react/no-array-index-key
<strong key={i} className={nameClassName}>
<Emojify text={group} />
</strong>
));
if (sharedGroupNames.length > 3) {
const remainingCount = sharedGroupNames.length - 3;
return (
<Intl
i18n={i18n}
id="member-of-more-than-3-groups"
components={{
group1: firstThreeGroups[0],
group2: firstThreeGroups[1],
group3: firstThreeGroups[2],
remainingCount: remainingCount.toString(),
}}
/>
);
}
if (firstThreeGroups.length === 3) {
return (
<Intl
i18n={i18n}
id="member-of-3-groups"
components={{
group1: firstThreeGroups[0],
group2: firstThreeGroups[1],
group3: firstThreeGroups[2],
}}
/>
);
}
if (firstThreeGroups.length >= 2) {
return (
<Intl
i18n={i18n}
id="member-of-2-groups"
components={{
group1: firstThreeGroups[0],
group2: firstThreeGroups[1],
}}
/>
);
}
if (firstThreeGroups.length >= 1) {
return (
<Intl
i18n={i18n}
id="member-of-1-group"
components={{
group: firstThreeGroups[0],
}}
/>
);
}
return <>{i18n('no-groups-in-common')}</>;
};

View file

@ -7,6 +7,7 @@ import { createPortal } from 'react-dom';
import { ConversationType } from '../../state/ducks/conversations'; import { ConversationType } from '../../state/ducks/conversations';
import { About } from './About'; import { About } from './About';
import { Avatar } from '../Avatar'; import { Avatar } from '../Avatar';
import { SharedGroupNames } from '../SharedGroupNames';
import { LocalizerType } from '../../types/Util'; import { LocalizerType } from '../../types/Util';
export type PropsType = { export type PropsType = {
@ -119,10 +120,16 @@ export const ContactModal = ({
<About text={contact.about} /> <About text={contact.about} />
</div> </div>
{contact.phoneNumber && ( {contact.phoneNumber && (
<div className="module-contact-modal__profile-and-number"> <div className="module-contact-modal__info">
{contact.phoneNumber} {contact.phoneNumber}
</div> </div>
)} )}
<div className="module-contact-modal__info">
<SharedGroupNames
i18n={i18n}
sharedGroupNames={contact.sharedGroupNames || []}
/>
</div>
<div className="module-contact-modal__button-container"> <div className="module-contact-modal__button-container">
<button <button
type="button" type="button"

View file

@ -2,12 +2,10 @@
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import * as React from 'react'; import * as React from 'react';
import { take } from 'lodash';
import { Avatar, Props as AvatarProps } from '../Avatar'; import { Avatar, Props as AvatarProps } from '../Avatar';
import { ContactName } from './ContactName'; import { ContactName } from './ContactName';
import { About } from './About'; import { About } from './About';
import { Emojify } from './Emojify'; import { SharedGroupNames } from '../SharedGroupNames';
import { Intl } from '../Intl';
import { LocalizerType } from '../../types/Util'; import { LocalizerType } from '../../types/Util';
export type Props = { export type Props = {
@ -32,7 +30,6 @@ const renderMembershipRow = ({
'i18n' | 'phoneNumber' | 'sharedGroupNames' | 'conversationType' | 'isMe' 'i18n' | 'phoneNumber' | 'sharedGroupNames' | 'conversationType' | 'isMe'
>) => { >) => {
const className = 'module-conversation-hero__membership'; const className = 'module-conversation-hero__membership';
const nameClassName = `${className}__name`;
if (conversationType !== 'direct') { if (conversationType !== 'direct') {
return null; return null;
@ -43,73 +40,15 @@ const renderMembershipRow = ({
} }
if (sharedGroupNames.length > 0) { if (sharedGroupNames.length > 0) {
const firstThreeGroups = take(sharedGroupNames, 3).map((group, i) => ( return (
// We cannot guarantee uniqueness of group names <div className={className}>
// eslint-disable-next-line react/no-array-index-key <SharedGroupNames
<strong key={i} className={nameClassName}> i18n={i18n}
<Emojify text={group} /> nameClassName={`${className}__name`}
</strong> sharedGroupNames={sharedGroupNames}
)); />
</div>
if (sharedGroupNames.length > 3) { );
const remainingCount = sharedGroupNames.length - 3;
return (
<div className={className}>
<Intl
i18n={i18n}
id="ConversationHero--membership-extra"
components={{
group1: firstThreeGroups[0],
group2: firstThreeGroups[1],
group3: firstThreeGroups[2],
remainingCount: remainingCount.toString(),
}}
/>
</div>
);
}
if (firstThreeGroups.length === 3) {
return (
<div className={className}>
<Intl
i18n={i18n}
id="ConversationHero--membership-3"
components={{
group1: firstThreeGroups[0],
group2: firstThreeGroups[1],
group3: firstThreeGroups[2],
}}
/>
</div>
);
}
if (firstThreeGroups.length >= 2) {
return (
<div className={className}>
<Intl
i18n={i18n}
id="ConversationHero--membership-2"
components={{
group1: firstThreeGroups[0],
group2: firstThreeGroups[1],
}}
/>
</div>
);
}
if (firstThreeGroups.length >= 1) {
return (
<div className={className}>
<Intl
i18n={i18n}
id="ConversationHero--membership-1"
components={{
group: firstThreeGroups[0],
}}
/>
</div>
);
}
} }
if (!phoneNumber) { if (!phoneNumber) {

View file

@ -16492,7 +16492,7 @@
"rule": "React-useRef", "rule": "React-useRef",
"path": "ts/components/conversation/ContactModal.js", "path": "ts/components/conversation/ContactModal.js",
"line": " const overlayRef = react_1.default.useRef(null);", "line": " const overlayRef = react_1.default.useRef(null);",
"lineNumber": 18, "lineNumber": 19,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2020-11-09T17:48:12.173Z" "updated": "2020-11-09T17:48:12.173Z"
}, },
@ -16500,7 +16500,7 @@
"rule": "React-useRef", "rule": "React-useRef",
"path": "ts/components/conversation/ContactModal.js", "path": "ts/components/conversation/ContactModal.js",
"line": " const closeButtonRef = react_1.default.useRef(null);", "line": " const closeButtonRef = react_1.default.useRef(null);",
"lineNumber": 19, "lineNumber": 20,
"reasonCategory": "usageTrusted", "reasonCategory": "usageTrusted",
"updated": "2020-11-10T21:27:04.909Z" "updated": "2020-11-10T21:27:04.909Z"
}, },
@ -16526,7 +16526,7 @@
"rule": "React-useRef", "rule": "React-useRef",
"path": "ts/components/conversation/ConversationHero.js", "path": "ts/components/conversation/ConversationHero.js",
"line": " const firstRenderRef = React.useRef(true);", "line": " const firstRenderRef = React.useRef(true);",
"lineNumber": 85, "lineNumber": 48,
"reasonCategory": "falseMatch", "reasonCategory": "falseMatch",
"updated": "2020-10-26T19:12:24.410Z", "updated": "2020-10-26T19:12:24.410Z",
"reasonDetail": "Doesn't refer to a DOM element." "reasonDetail": "Doesn't refer to a DOM element."
@ -16535,7 +16535,7 @@
"rule": "React-useRef", "rule": "React-useRef",
"path": "ts/components/conversation/ConversationHero.tsx", "path": "ts/components/conversation/ConversationHero.tsx",
"line": " const firstRenderRef = React.useRef(true);", "line": " const firstRenderRef = React.useRef(true);",
"lineNumber": 138, "lineNumber": 77,
"reasonCategory": "falseMatch", "reasonCategory": "falseMatch",
"updated": "2020-10-26T19:12:24.410Z", "updated": "2020-10-26T19:12:24.410Z",
"reasonDetail": "Doesn't refer to a DOM element." "reasonDetail": "Doesn't refer to a DOM element."