Add badges to avatars in group dialogs

This commit is contained in:
Evan Hahn 2021-11-20 09:41:21 -06:00 committed by GitHub
parent 7bb37dc63b
commit e490d91cc4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 121 additions and 39 deletions

View file

@ -4,8 +4,9 @@
import type { ReactChild, ReactNode } from 'react'; import type { ReactChild, ReactNode } from 'react';
import React from 'react'; import React from 'react';
import type { LocalizerType } from '../types/Util'; import type { LocalizerType, ThemeType } from '../types/Util';
import type { ConversationType } from '../state/ducks/conversations'; import type { ConversationType } from '../state/ducks/conversations';
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
import { ModalHost } from './ModalHost'; import { ModalHost } from './ModalHost';
import { Button, ButtonVariant } from './Button'; import { Button, ButtonVariant } from './Button';
import { Avatar, AvatarSize } from './Avatar'; import { Avatar, AvatarSize } from './Avatar';
@ -92,20 +93,29 @@ GroupDialog.Paragraph = ({
type ContactsPropsType = { type ContactsPropsType = {
contacts: Array<ConversationType>; contacts: Array<ConversationType>;
getPreferredBadge: PreferredBadgeSelectorType;
i18n: LocalizerType; i18n: LocalizerType;
theme: ThemeType;
}; };
GroupDialog.Contacts = ({ contacts, i18n }: Readonly<ContactsPropsType>) => ( GroupDialog.Contacts = ({
contacts,
getPreferredBadge,
i18n,
theme,
}: Readonly<ContactsPropsType>) => (
<ul className="module-GroupDialog__contacts"> <ul className="module-GroupDialog__contacts">
{contacts.map(contact => ( {contacts.map(contact => (
<li key={contact.id} className="module-GroupDialog__contacts__contact"> <li key={contact.id} className="module-GroupDialog__contacts__contact">
<Avatar <Avatar
acceptedMessageRequest={contact.acceptedMessageRequest} acceptedMessageRequest={contact.acceptedMessageRequest}
avatarPath={contact.avatarPath} avatarPath={contact.avatarPath}
badge={getPreferredBadge(contact.badges)}
color={contact.color} color={contact.color}
conversationType={contact.type} conversationType={contact.type}
isMe={contact.isMe} isMe={contact.isMe}
noteToSelf={contact.isMe} noteToSelf={contact.isMe}
theme={theme}
title={contact.title} title={contact.title}
unblurredAvatarPath={contact.unblurredAvatarPath} unblurredAvatarPath={contact.unblurredAvatarPath}
sharedGroupNames={contact.sharedGroupNames} sharedGroupNames={contact.sharedGroupNames}

View file

@ -13,6 +13,7 @@ import type { ConversationType } from '../state/ducks/conversations';
import { setupI18n } from '../util/setupI18n'; import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json'; import enMessages from '../../_locales/en/messages.json';
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation'; import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
import { ThemeType } from '../types/Util';
const i18n = setupI18n('en', enMessages); const i18n = setupI18n('en', enMessages);
@ -44,6 +45,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
booleanOr(overrideProps.areWeInvited, false) booleanOr(overrideProps.areWeInvited, false)
), ),
droppedMembers: overrideProps.droppedMembers || [contact3, contact1], droppedMembers: overrideProps.droppedMembers || [contact3, contact1],
getPreferredBadge: () => undefined,
hasMigrated: boolean( hasMigrated: boolean(
'hasMigrated', 'hasMigrated',
booleanOr(overrideProps.hasMigrated, false) booleanOr(overrideProps.hasMigrated, false)
@ -52,6 +54,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
invitedMembers: overrideProps.invitedMembers || [contact2], invitedMembers: overrideProps.invitedMembers || [contact2],
migrate: action('migrate'), migrate: action('migrate'),
onClose: action('onClose'), onClose: action('onClose'),
theme: ThemeType.light,
}); });
const stories = storiesOf('Components/GroupV1MigrationDialog', module); const stories = storiesOf('Components/GroupV1MigrationDialog', module);

View file

@ -1,9 +1,10 @@
// Copyright 2019-2020 Signal Messenger, LLC // Copyright 2019-2021 Signal Messenger, LLC
// 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 type { LocalizerType } from '../types/Util'; import type { LocalizerType, ThemeType } from '../types/Util';
import type { ConversationType } from '../state/ducks/conversations'; import type { ConversationType } from '../state/ducks/conversations';
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
import { GroupDialog } from './GroupDialog'; import { GroupDialog } from './GroupDialog';
import { sortByTitle } from '../util/sortByTitle'; import { sortByTitle } from '../util/sortByTitle';
@ -19,7 +20,9 @@ export type DataPropsType = {
}; };
export type HousekeepingPropsType = { export type HousekeepingPropsType = {
readonly getPreferredBadge: PreferredBadgeSelectorType;
readonly i18n: LocalizerType; readonly i18n: LocalizerType;
readonly theme: ThemeType;
}; };
export type PropsType = DataPropsType & HousekeepingPropsType; export type PropsType = DataPropsType & HousekeepingPropsType;
@ -29,11 +32,13 @@ export const GroupV1MigrationDialog: React.FunctionComponent<PropsType> =
const { const {
areWeInvited, areWeInvited,
droppedMembers, droppedMembers,
getPreferredBadge,
hasMigrated, hasMigrated,
i18n, i18n,
invitedMembers, invitedMembers,
migrate, migrate,
onClose, onClose,
theme,
} = props; } = props;
const title = hasMigrated const title = hasMigrated
@ -84,23 +89,39 @@ export const GroupV1MigrationDialog: React.FunctionComponent<PropsType> =
</GroupDialog.Paragraph> </GroupDialog.Paragraph>
) : ( ) : (
<> <>
{renderMembers( {renderMembers({
invitedMembers, getPreferredBadge,
'GroupV1--Migration--info--invited', i18n,
i18n members: invitedMembers,
)} prefix: 'GroupV1--Migration--info--invited',
{renderMembers(droppedMembers, droppedMembersKey, i18n)} theme,
})}
{renderMembers({
getPreferredBadge,
i18n,
members: droppedMembers,
prefix: droppedMembersKey,
theme,
})}
</> </>
)} )}
</GroupDialog> </GroupDialog>
); );
}); });
function renderMembers( function renderMembers({
members: Array<ConversationType>, getPreferredBadge,
prefix: string, i18n,
i18n: LocalizerType members,
): React.ReactNode { prefix,
theme,
}: Readonly<{
getPreferredBadge: PreferredBadgeSelectorType;
i18n: LocalizerType;
members: Array<ConversationType>;
prefix: string;
theme: ThemeType;
}>): React.ReactNode {
if (!members.length) { if (!members.length) {
return null; return null;
} }
@ -111,7 +132,12 @@ function renderMembers(
return ( return (
<> <>
<GroupDialog.Paragraph>{i18n(key)}</GroupDialog.Paragraph> <GroupDialog.Paragraph>{i18n(key)}</GroupDialog.Paragraph>
<GroupDialog.Contacts contacts={sortByTitle(members)} i18n={i18n} /> <GroupDialog.Contacts
contacts={sortByTitle(members)}
getPreferredBadge={getPreferredBadge}
i18n={i18n}
theme={theme}
/>
</> </>
); );
} }

View file

@ -11,6 +11,7 @@ import { setupI18n } from '../util/setupI18n';
import enMessages from '../../_locales/en/messages.json'; import enMessages from '../../_locales/en/messages.json';
import type { ConversationType } from '../state/ducks/conversations'; import type { ConversationType } from '../state/ducks/conversations';
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation'; import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
import { ThemeType } from '../types/Util';
const i18n = setupI18n('en', enMessages); const i18n = setupI18n('en', enMessages);
@ -27,15 +28,19 @@ const story = storiesOf(
story.add('One contact', () => ( story.add('One contact', () => (
<NewlyCreatedGroupInvitedContactsDialog <NewlyCreatedGroupInvitedContactsDialog
contacts={[conversations[0]]} contacts={[conversations[0]]}
getPreferredBadge={() => undefined}
i18n={i18n} i18n={i18n}
onClose={action('onClose')} onClose={action('onClose')}
theme={ThemeType.light}
/> />
)); ));
story.add('Two contacts', () => ( story.add('Two contacts', () => (
<NewlyCreatedGroupInvitedContactsDialog <NewlyCreatedGroupInvitedContactsDialog
contacts={conversations} contacts={conversations}
getPreferredBadge={() => undefined}
i18n={i18n} i18n={i18n}
onClose={action('onClose')} onClose={action('onClose')}
theme={ThemeType.light}
/> />
)); ));

View file

@ -4,8 +4,9 @@
import type { FunctionComponent, ReactNode } from 'react'; import type { FunctionComponent, ReactNode } from 'react';
import React from 'react'; import React from 'react';
import type { LocalizerType } from '../types/Util'; import type { LocalizerType, ThemeType } from '../types/Util';
import type { ConversationType } from '../state/ducks/conversations'; import type { ConversationType } from '../state/ducks/conversations';
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
import { Intl } from './Intl'; import { Intl } from './Intl';
import { ContactName } from './conversation/ContactName'; import { ContactName } from './conversation/ContactName';
import { GroupDialog } from './GroupDialog'; import { GroupDialog } from './GroupDialog';
@ -13,12 +14,14 @@ import { openLinkInWebBrowser } from '../util/openLinkInWebBrowser';
type PropsType = { type PropsType = {
contacts: Array<ConversationType>; contacts: Array<ConversationType>;
getPreferredBadge: PreferredBadgeSelectorType;
i18n: LocalizerType; i18n: LocalizerType;
onClose: () => void; onClose: () => void;
theme: ThemeType;
}; };
export const NewlyCreatedGroupInvitedContactsDialog: FunctionComponent<PropsType> = export const NewlyCreatedGroupInvitedContactsDialog: FunctionComponent<PropsType> =
({ contacts, i18n, onClose }) => { ({ contacts, getPreferredBadge, i18n, onClose, theme }) => {
let title: string; let title: string;
let body: ReactNode; let body: ReactNode;
if (contacts.length === 1) { if (contacts.length === 1) {
@ -57,7 +60,12 @@ export const NewlyCreatedGroupInvitedContactsDialog: FunctionComponent<PropsType
'NewlyCreatedGroupInvitedContactsDialog--body--info-paragraph' 'NewlyCreatedGroupInvitedContactsDialog--body--info-paragraph'
)} )}
</GroupDialog.Paragraph> </GroupDialog.Paragraph>
<GroupDialog.Contacts contacts={contacts} i18n={i18n} /> <GroupDialog.Contacts
contacts={contacts}
getPreferredBadge={getPreferredBadge}
i18n={i18n}
theme={theme}
/>
</> </>
); );
} }

View file

@ -12,6 +12,7 @@ import { setupI18n } from '../../util/setupI18n';
import enMessages from '../../../_locales/en/messages.json'; import enMessages from '../../../_locales/en/messages.json';
import type { PropsType } from './GroupV1Migration'; import type { PropsType } from './GroupV1Migration';
import { GroupV1Migration } from './GroupV1Migration'; import { GroupV1Migration } from './GroupV1Migration';
import { ThemeType } from '../../types/Util';
const i18n = setupI18n('en', enMessages); const i18n = setupI18n('en', enMessages);
@ -33,8 +34,10 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
isBoolean(overrideProps.areWeInvited) ? overrideProps.areWeInvited : false isBoolean(overrideProps.areWeInvited) ? overrideProps.areWeInvited : false
), ),
droppedMembers: overrideProps.droppedMembers || [contact1], droppedMembers: overrideProps.droppedMembers || [contact1],
getPreferredBadge: () => undefined,
i18n, i18n,
invitedMembers: overrideProps.invitedMembers || [contact2], invitedMembers: overrideProps.invitedMembers || [contact2],
theme: ThemeType.light,
}); });
const stories = storiesOf('Components/Conversation/GroupV1Migration', module); const stories = storiesOf('Components/Conversation/GroupV1Migration', module);

View file

@ -5,8 +5,9 @@ import * as React from 'react';
import { Button, ButtonSize, ButtonVariant } from '../Button'; import { Button, ButtonSize, ButtonVariant } from '../Button';
import { SystemMessage } from './SystemMessage'; import { SystemMessage } from './SystemMessage';
import type { LocalizerType } from '../../types/Util'; import type { LocalizerType, ThemeType } from '../../types/Util';
import type { ConversationType } from '../../state/ducks/conversations'; import type { ConversationType } from '../../state/ducks/conversations';
import type { PreferredBadgeSelectorType } from '../../state/selectors/badges';
import { Intl } from '../Intl'; import { Intl } from '../Intl';
import { ContactName } from './ContactName'; import { ContactName } from './ContactName';
import { GroupV1MigrationDialog } from '../GroupV1MigrationDialog'; import { GroupV1MigrationDialog } from '../GroupV1MigrationDialog';
@ -19,13 +20,22 @@ export type PropsDataType = {
}; };
export type PropsHousekeepingType = { export type PropsHousekeepingType = {
getPreferredBadge: PreferredBadgeSelectorType;
i18n: LocalizerType; i18n: LocalizerType;
theme: ThemeType;
}; };
export type PropsType = PropsDataType & PropsHousekeepingType; export type PropsType = PropsDataType & PropsHousekeepingType;
export function GroupV1Migration(props: PropsType): React.ReactElement { export function GroupV1Migration(props: PropsType): React.ReactElement {
const { areWeInvited, droppedMembers, i18n, invitedMembers } = props; const {
areWeInvited,
droppedMembers,
getPreferredBadge,
i18n,
invitedMembers,
theme,
} = props;
const [showingDialog, setShowingDialog] = React.useState(false); const [showingDialog, setShowingDialog] = React.useState(false);
const showDialog = React.useCallback(() => { const showDialog = React.useCallback(() => {
@ -77,11 +87,13 @@ export function GroupV1Migration(props: PropsType): React.ReactElement {
<GroupV1MigrationDialog <GroupV1MigrationDialog
areWeInvited={areWeInvited} areWeInvited={areWeInvited}
droppedMembers={droppedMembers} droppedMembers={droppedMembers}
getPreferredBadge={getPreferredBadge}
hasMigrated hasMigrated
i18n={i18n} i18n={i18n}
invitedMembers={invitedMembers} invitedMembers={invitedMembers}
migrate={() => log.warn('GroupV1Migration: Modal called migrate()')} migrate={() => log.warn('GroupV1Migration: Modal called migrate()')}
onClose={dismissDialog} onClose={dismissDialog}
theme={theme}
/> />
) : null} ) : null}
</> </>

View file

@ -457,8 +457,10 @@ const renderTypingBubble = () => (
/> />
); );
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
getPreferredBadge: () => undefined,
i18n, i18n,
theme: React.useContext(StorybookThemeContext),
haveNewest: boolean('haveNewest', overrideProps.haveNewest !== false), haveNewest: boolean('haveNewest', overrideProps.haveNewest !== false),
haveOldest: boolean('haveOldest', overrideProps.haveOldest !== false), haveOldest: boolean('haveOldest', overrideProps.haveOldest !== false),
@ -494,13 +496,13 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
}); });
story.add('Oldest and Newest', () => { story.add('Oldest and Newest', () => {
const props = createProps(); const props = useProps();
return <Timeline {...props} />; return <Timeline {...props} />;
}); });
story.add('With active message request', () => { story.add('With active message request', () => {
const props = createProps({ const props = useProps({
isIncomingMessageRequest: true, isIncomingMessageRequest: true,
}); });
@ -508,7 +510,7 @@ story.add('With active message request', () => {
}); });
story.add('Without Newest Message', () => { story.add('Without Newest Message', () => {
const props = createProps({ const props = useProps({
haveNewest: false, haveNewest: false,
}); });
@ -516,7 +518,7 @@ story.add('Without Newest Message', () => {
}); });
story.add('Without newest message, active message request', () => { story.add('Without newest message, active message request', () => {
const props = createProps({ const props = useProps({
haveOldest: false, haveOldest: false,
isIncomingMessageRequest: true, isIncomingMessageRequest: true,
}); });
@ -525,7 +527,7 @@ story.add('Without newest message, active message request', () => {
}); });
story.add('Without Oldest Message', () => { story.add('Without Oldest Message', () => {
const props = createProps({ const props = useProps({
haveOldest: false, haveOldest: false,
scrollToIndex: -1, scrollToIndex: -1,
}); });
@ -534,7 +536,7 @@ story.add('Without Oldest Message', () => {
}); });
story.add('Empty (just hero)', () => { story.add('Empty (just hero)', () => {
const props = createProps({ const props = useProps({
items: [], items: [],
}); });
@ -542,7 +544,7 @@ story.add('Empty (just hero)', () => {
}); });
story.add('Last Seen', () => { story.add('Last Seen', () => {
const props = createProps({ const props = useProps({
oldestUnreadIndex: 13, oldestUnreadIndex: 13,
totalUnread: 2, totalUnread: 2,
}); });
@ -551,7 +553,7 @@ story.add('Last Seen', () => {
}); });
story.add('Target Index to Top', () => { story.add('Target Index to Top', () => {
const props = createProps({ const props = useProps({
scrollToIndex: 0, scrollToIndex: 0,
}); });
@ -559,7 +561,7 @@ story.add('Target Index to Top', () => {
}); });
story.add('Typing Indicator', () => { story.add('Typing Indicator', () => {
const props = createProps({ const props = useProps({
typingContactId: UUID.generate().toString(), typingContactId: UUID.generate().toString(),
}); });
@ -567,7 +569,7 @@ story.add('Typing Indicator', () => {
}); });
story.add('With invited contacts for a newly-created group', () => { story.add('With invited contacts for a newly-created group', () => {
const props = createProps({ const props = useProps({
invitedContactsForNewlyCreatedGroup: [ invitedContactsForNewlyCreatedGroup: [
getDefaultConversation({ getDefaultConversation({
id: 'abc123', id: 'abc123',
@ -584,7 +586,7 @@ story.add('With invited contacts for a newly-created group', () => {
}); });
story.add('With "same name in direct conversation" warning', () => { story.add('With "same name in direct conversation" warning', () => {
const props = createProps({ const props = useProps({
warning: { warning: {
type: ContactSpoofingType.DirectConversationWithSameTitle, type: ContactSpoofingType.DirectConversationWithSameTitle,
safeConversation: getDefaultConversation(), safeConversation: getDefaultConversation(),
@ -596,7 +598,7 @@ story.add('With "same name in direct conversation" warning', () => {
}); });
story.add('With "same name in group conversation" warning', () => { story.add('With "same name in group conversation" warning', () => {
const props = createProps({ const props = useProps({
warning: { warning: {
type: ContactSpoofingType.MultipleGroupMembersWithSameTitle, type: ContactSpoofingType.MultipleGroupMembersWithSameTitle,
acknowledgedGroupNameCollisions: {}, acknowledgedGroupNameCollisions: {},

View file

@ -17,8 +17,9 @@ import Measure from 'react-measure';
import { ScrollDownButton } from './ScrollDownButton'; import { ScrollDownButton } from './ScrollDownButton';
import type { AssertProps, LocalizerType } from '../../types/Util'; import type { AssertProps, LocalizerType, ThemeType } from '../../types/Util';
import type { ConversationType } from '../../state/ducks/conversations'; import type { ConversationType } from '../../state/ducks/conversations';
import type { PreferredBadgeSelectorType } from '../../state/selectors/badges';
import { assert } from '../../util/assert'; import { assert } from '../../util/assert';
import { missingCaseError } from '../../util/missingCaseError'; import { missingCaseError } from '../../util/missingCaseError';
import { createRefMerger } from '../../util/refMerger'; import { createRefMerger } from '../../util/refMerger';
@ -102,7 +103,9 @@ type PropsHousekeepingType = {
warning?: WarningType; warning?: WarningType;
contactSpoofingReview?: ContactSpoofingReviewPropType; contactSpoofingReview?: ContactSpoofingReviewPropType;
getPreferredBadge: PreferredBadgeSelectorType;
i18n: LocalizerType; i18n: LocalizerType;
theme: ThemeType;
renderItem: (props: { renderItem: (props: {
actionProps: PropsActionsType; actionProps: PropsActionsType;
@ -1312,6 +1315,7 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
clearInvitedUuidsForNewlyCreatedGroup, clearInvitedUuidsForNewlyCreatedGroup,
closeContactSpoofingReview, closeContactSpoofingReview,
contactSpoofingReview, contactSpoofingReview,
getPreferredBadge,
i18n, i18n,
id, id,
invitedContactsForNewlyCreatedGroup, invitedContactsForNewlyCreatedGroup,
@ -1325,6 +1329,7 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
removeMember, removeMember,
reviewGroupMemberNameCollision, reviewGroupMemberNameCollision,
reviewMessageRequestNameCollision, reviewMessageRequestNameCollision,
theme,
} = this.props; } = this.props;
const { const {
shouldShowScrollDownButton, shouldShowScrollDownButton,
@ -1561,8 +1566,10 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
{Boolean(invitedContactsForNewlyCreatedGroup.length) && ( {Boolean(invitedContactsForNewlyCreatedGroup.length) && (
<NewlyCreatedGroupInvitedContactsDialog <NewlyCreatedGroupInvitedContactsDialog
contacts={invitedContactsForNewlyCreatedGroup} contacts={invitedContactsForNewlyCreatedGroup}
getPreferredBadge={getPreferredBadge}
i18n={i18n} i18n={i18n}
onClose={clearInvitedUuidsForNewlyCreatedGroup} onClose={clearInvitedUuidsForNewlyCreatedGroup}
theme={theme}
/> />
)} )}

View file

@ -1,4 +1,4 @@
// Copyright 2020 Signal Messenger, LLC // Copyright 2020-2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import { connect } from 'react-redux'; import { connect } from 'react-redux';
@ -7,9 +7,10 @@ import type { PropsType as GroupV1MigrationDialogPropsType } from '../../compone
import { GroupV1MigrationDialog } from '../../components/GroupV1MigrationDialog'; import { GroupV1MigrationDialog } from '../../components/GroupV1MigrationDialog';
import type { ConversationType } from '../ducks/conversations'; import type { ConversationType } from '../ducks/conversations';
import type { StateType } from '../reducer'; import type { StateType } from '../reducer';
import { getPreferredBadgeSelector } from '../selectors/badges';
import { getConversationSelector } from '../selectors/conversations'; import { getConversationSelector } from '../selectors/conversations';
import { getIntl } from '../selectors/user'; import { getIntl, getTheme } from '../selectors/user';
import * as log from '../../logging/log'; import * as log from '../../logging/log';
export type PropsType = { export type PropsType = {
@ -17,7 +18,7 @@ export type PropsType = {
readonly invitedMemberIds: Array<string>; readonly invitedMemberIds: Array<string>;
} & Omit< } & Omit<
GroupV1MigrationDialogPropsType, GroupV1MigrationDialogPropsType,
'i18n' | 'droppedMembers' | 'invitedMembers' 'i18n' | 'droppedMembers' | 'invitedMembers' | 'theme' | 'getPreferredBadge'
>; >;
const mapStateToProps = ( const mapStateToProps = (
@ -44,8 +45,10 @@ const mapStateToProps = (
return { return {
...props, ...props,
droppedMembers, droppedMembers,
getPreferredBadge: getPreferredBadgeSelector(state),
invitedMembers, invitedMembers,
i18n: getIntl(state), i18n: getIntl(state),
theme: getTheme(state),
}; };
}; };

View file

@ -18,7 +18,7 @@ import { Timeline } from '../../components/conversation/Timeline';
import type { StateType } from '../reducer'; import type { StateType } from '../reducer';
import type { ConversationType } from '../ducks/conversations'; import type { ConversationType } from '../ducks/conversations';
import { getIntl } from '../selectors/user'; import { getIntl, getTheme } from '../selectors/user';
import { import {
getConversationByUuidSelector, getConversationByUuidSelector,
getConversationMessagesSelector, getConversationMessagesSelector,
@ -48,6 +48,7 @@ import {
} from '../../util/groupMemberNameCollisions'; } from '../../util/groupMemberNameCollisions';
import { ContactSpoofingType } from '../../util/contactSpoofing'; import { ContactSpoofingType } from '../../util/contactSpoofing';
import type { WidthBreakpoint } from '../../components/_util'; import type { WidthBreakpoint } from '../../components/_util';
import { getPreferredBadgeSelector } from '../selectors/badges';
type ExternalProps = { type ExternalProps = {
id: string; id: string;
@ -313,7 +314,9 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
warning: getWarning(conversation, state), warning: getWarning(conversation, state),
contactSpoofingReview: getContactSpoofingReview(id, state), contactSpoofingReview: getContactSpoofingReview(id, state),
getPreferredBadge: getPreferredBadgeSelector(state),
i18n: getIntl(state), i18n: getIntl(state),
theme: getTheme(state),
renderItem, renderItem,
renderLastSeenIndicator, renderLastSeenIndicator,
renderHeroRow, renderHeroRow,