Add user badges to typing bubbles, refactor typing logic

This commit is contained in:
Evan Hahn 2021-11-15 14:01:58 -06:00 committed by GitHub
parent ede34ecee3
commit f4e336836f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 125 additions and 189 deletions

View file

@ -41,7 +41,6 @@ export const AnnouncementsOnlyGroupBanner = ({
draftPreview="" draftPreview=""
lastMessage={undefined} lastMessage={undefined}
lastUpdated={undefined} lastUpdated={undefined}
typingContact={undefined}
theme={theme} theme={theme}
/> />
))} ))}

View file

@ -18,6 +18,7 @@ import { getDefaultConversation } from '../test-both/helpers/getDefaultConversat
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 { StorybookThemeContext } from '../../.storybook/StorybookThemeContext'; import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext';
import { UUID } from '../types/UUID';
const i18n = setupI18n('en', enMessages); const i18n = setupI18n('en', enMessages);
@ -295,10 +296,7 @@ story.add('Contact checkboxes: disabled', () => (
story.add('Conversation: Typing Status', () => story.add('Conversation: Typing Status', () =>
renderConversation({ renderConversation({
typingContact: { typingContactId: UUID.generate().toString(),
...getDefaultConversation(),
name: 'Someone Here',
},
}) })
); );

View file

@ -269,7 +269,7 @@ export const ConversationList: React.FC<PropsType> = ({
'shouldShowDraft', 'shouldShowDraft',
'title', 'title',
'type', 'type',
'typingContact', 'typingContactId',
'unblurredAvatarPath', 'unblurredAvatarPath',
'unreadCount', 'unreadCount',
]); ]);

View file

@ -3,7 +3,7 @@
import * as React from 'react'; import * as React from 'react';
import * as moment from 'moment'; import * as moment from 'moment';
import { isBoolean, times } from 'lodash'; import { times } from 'lodash';
import { v4 as uuid } from 'uuid'; import { v4 as uuid } from 'uuid';
import { storiesOf } from '@storybook/react'; import { storiesOf } from '@storybook/react';
import { text, boolean, number } from '@storybook/addon-knobs'; import { text, boolean, number } from '@storybook/addon-knobs';
@ -25,6 +25,8 @@ import { TypingBubble } from './TypingBubble';
import { ContactSpoofingType } from '../../util/contactSpoofing'; import { ContactSpoofingType } from '../../util/contactSpoofing';
import { ReadStatus } from '../../messages/MessageReadStatus'; import { ReadStatus } from '../../messages/MessageReadStatus';
import type { WidthBreakpoint } from '../_util'; import type { WidthBreakpoint } from '../_util';
import { ThemeType } from '../../types/Util';
import { UUID } from '../../types/UUID';
const i18n = setupI18n('en', enMessages); const i18n = setupI18n('en', enMessages);
@ -441,12 +443,14 @@ const renderLoadingRow = () => <TimelineLoadingRow state="loading" />;
const renderTypingBubble = () => ( const renderTypingBubble = () => (
<TypingBubble <TypingBubble
acceptedMessageRequest acceptedMessageRequest
badge={undefined}
color={getRandomColor()} color={getRandomColor()}
conversationType="direct" conversationType="direct"
phoneNumber="+18005552222" phoneNumber="+18005552222"
i18n={i18n} i18n={i18n}
isMe={false} isMe={false}
title="title" title="title"
theme={ThemeType.light}
sharedGroupNames={[]} sharedGroupNames={[]}
/> />
); );
@ -486,10 +490,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
renderHeroRow, renderHeroRow,
renderLoadingRow, renderLoadingRow,
renderTypingBubble, renderTypingBubble,
typingContact: boolean( typingContactId: overrideProps.typingContactId,
'typingContact',
isBoolean(overrideProps.typingContact) ? overrideProps.typingContact : false
),
...actions(), ...actions(),
}); });
@ -561,7 +562,7 @@ story.add('Target Index to Top', () => {
story.add('Typing Indicator', () => { story.add('Typing Indicator', () => {
const props = createProps({ const props = createProps({
typingContact: true, typingContactId: UUID.generate().toString(),
}); });
return <Timeline {...props} />; return <Timeline {...props} />;

View file

@ -94,7 +94,7 @@ type PropsHousekeepingType = {
areWeAdmin?: boolean; areWeAdmin?: boolean;
isGroupV1AndDisabled?: boolean; isGroupV1AndDisabled?: boolean;
isIncomingMessageRequest: boolean; isIncomingMessageRequest: boolean;
typingContact?: unknown; typingContactId?: string;
unreadCount?: number; unreadCount?: number;
selectedMessageId?: string; selectedMessageId?: string;
@ -859,7 +859,7 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
} }
public getRowCount(): number { public getRowCount(): number {
const { oldestUnreadIndex, typingContact } = this.props; const { oldestUnreadIndex, typingContactId } = this.props;
const { items } = this.props; const { items } = this.props;
const itemsCount = items && items.length ? items.length : 0; const itemsCount = items && items.length ? items.length : 0;
@ -870,7 +870,7 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
extraRows += 1; extraRows += 1;
} }
if (typingContact) { if (typingContactId) {
extraRows += 1; extraRows += 1;
} }
@ -1033,7 +1033,7 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
resetCounter, resetCounter,
scrollToBottomCounter, scrollToBottomCounter,
scrollToIndex, scrollToIndex,
typingContact, typingContactId,
} = this.props; } = this.props;
// We recompute the hero row's height if: // We recompute the hero row's height if:
@ -1097,7 +1097,7 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
if ( if (
items !== prevProps.items || items !== prevProps.items ||
oldestUnreadIndex !== prevProps.oldestUnreadIndex || oldestUnreadIndex !== prevProps.oldestUnreadIndex ||
Boolean(typingContact) !== Boolean(prevProps.typingContact) Boolean(typingContactId) !== Boolean(prevProps.typingContactId)
) { ) {
const { atTop } = this.state; const { atTop } = this.state;
@ -1135,13 +1135,13 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
const rowsIterator = Timeline.getEphemeralRows({ const rowsIterator = Timeline.getEphemeralRows({
items, items,
oldestUnreadIndex, oldestUnreadIndex,
typingContact: Boolean(typingContact), hasTypingContact: Boolean(typingContactId),
haveOldest, haveOldest,
}); });
const prevRowsIterator = Timeline.getEphemeralRows({ const prevRowsIterator = Timeline.getEphemeralRows({
items: prevProps.items, items: prevProps.items,
oldestUnreadIndex: prevProps.oldestUnreadIndex, oldestUnreadIndex: prevProps.oldestUnreadIndex,
typingContact: Boolean(prevProps.typingContact), hasTypingContact: Boolean(prevProps.typingContactId),
haveOldest: prevProps.haveOldest, haveOldest: prevProps.haveOldest,
}); });
@ -1578,13 +1578,13 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
} }
private static *getEphemeralRows({ private static *getEphemeralRows({
items, hasTypingContact,
typingContact,
oldestUnreadIndex,
haveOldest, haveOldest,
items,
oldestUnreadIndex,
}: { }: {
items: ReadonlyArray<string>; items: ReadonlyArray<string>;
typingContact: boolean; hasTypingContact: boolean;
oldestUnreadIndex?: number; oldestUnreadIndex?: number;
haveOldest: boolean; haveOldest: boolean;
}): Iterator<string> { }): Iterator<string> {
@ -1597,7 +1597,7 @@ export class Timeline extends React.PureComponent<PropsType, StateType> {
yield `item:${items[i]}`; yield `item:${items[i]}`;
} }
if (typingContact) { if (hasTypingContact) {
yield 'typing-contact'; yield 'typing-contact';
} }
} }

View file

@ -10,6 +10,8 @@ import enMessages from '../../../_locales/en/messages.json';
import type { Props } from './TypingBubble'; import type { Props } from './TypingBubble';
import { TypingBubble } from './TypingBubble'; import { TypingBubble } from './TypingBubble';
import { AvatarColors } from '../../types/Colors'; import { AvatarColors } from '../../types/Colors';
import { getFakeBadge } from '../../test-both/helpers/getFakeBadge';
import { ThemeType } from '../../types/Util';
const i18n = setupI18n('en', enMessages); const i18n = setupI18n('en', enMessages);
@ -17,6 +19,7 @@ const story = storiesOf('Components/Conversation/TypingBubble', module);
const createProps = (overrideProps: Partial<Props> = {}): Props => ({ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
acceptedMessageRequest: true, acceptedMessageRequest: true,
badge: overrideProps.badge,
isMe: false, isMe: false,
i18n, i18n,
color: select( color: select(
@ -33,6 +36,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
overrideProps.conversationType || 'direct' overrideProps.conversationType || 'direct'
), ),
sharedGroupNames: [], sharedGroupNames: [],
theme: ThemeType.light,
}); });
story.add('Direct', () => { story.add('Direct', () => {
@ -46,3 +50,12 @@ story.add('Group', () => {
return <TypingBubble {...props} />; return <TypingBubble {...props} />;
}); });
story.add('Group (with badge)', () => {
const props = createProps({
badge: getFakeBadge(),
conversationType: 'group',
});
return <TypingBubble {...props} />;
});

View file

@ -1,14 +1,16 @@
// Copyright 2018-2021 Signal Messenger, LLC // Copyright 2018-2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only // SPDX-License-Identifier: AGPL-3.0-only
import type { ReactElement } from 'react';
import React from 'react'; import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { TypingAnimation } from './TypingAnimation'; import { TypingAnimation } from './TypingAnimation';
import { Avatar } from '../Avatar'; import { Avatar } from '../Avatar';
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 { BadgeType } from '../../badges/types';
export type Props = Pick< export type Props = Pick<
ConversationType, ConversationType,
@ -22,76 +24,69 @@ export type Props = Pick<
| 'sharedGroupNames' | 'sharedGroupNames'
| 'title' | 'title'
> & { > & {
badge: undefined | BadgeType;
conversationType: 'group' | 'direct'; conversationType: 'group' | 'direct';
i18n: LocalizerType; i18n: LocalizerType;
theme: ThemeType;
}; };
export class TypingBubble extends React.PureComponent<Props> { export function TypingBubble({
public renderAvatar(): JSX.Element | null { acceptedMessageRequest,
const { avatarPath,
acceptedMessageRequest, badge,
avatarPath, color,
color, conversationType,
conversationType, i18n,
i18n, isMe,
isMe, name,
name, phoneNumber,
phoneNumber, profileName,
profileName, sharedGroupNames,
sharedGroupNames, theme,
title, title,
} = this.props; }: Props): ReactElement {
const isGroup = conversationType === 'group';
if (conversationType !== 'group') { return (
return null; <div
} className={classNames(
'module-message',
return ( 'module-message--incoming',
<div className="module-message__author-avatar-container"> isGroup ? 'module-message--group' : null
<Avatar )}
acceptedMessageRequest={acceptedMessageRequest} >
avatarPath={avatarPath} {isGroup && (
color={color} <div className="module-message__author-avatar-container">
conversationType="direct" <Avatar
i18n={i18n} acceptedMessageRequest={acceptedMessageRequest}
isMe={isMe} avatarPath={avatarPath}
name={name} badge={badge}
phoneNumber={phoneNumber} color={color}
profileName={profileName} conversationType="direct"
title={title} i18n={i18n}
sharedGroupNames={sharedGroupNames} isMe={isMe}
size={28} name={name}
/> phoneNumber={phoneNumber}
</div> profileName={profileName}
); theme={theme}
} title={title}
sharedGroupNames={sharedGroupNames}
public override render(): JSX.Element { size={28}
const { i18n, conversationType } = this.props; />
const isGroup = conversationType === 'group'; </div>
)}
return ( <div className="module-message__container-outer">
<div <div
className={classNames( className={classNames(
'module-message', 'module-message__container',
'module-message--incoming', 'module-message__container--incoming'
isGroup ? 'module-message--group' : null )}
)} >
> <div className="module-message__typing-container">
{this.renderAvatar()} <TypingAnimation color="light" i18n={i18n} />
<div className="module-message__container-outer">
<div
className={classNames(
'module-message__container',
'module-message__container--incoming'
)}
>
<div className="module-message__typing-container">
<TypingAnimation color="light" i18n={i18n} />
</div>
</div> </div>
</div> </div>
</div> </div>
); </div>
} );
} }

View file

@ -55,7 +55,7 @@ export type PropsData = Pick<
| 'shouldShowDraft' | 'shouldShowDraft'
| 'title' | 'title'
| 'type' | 'type'
| 'typingContact' | 'typingContactId'
| 'unblurredAvatarPath' | 'unblurredAvatarPath'
| 'unreadCount' | 'unreadCount'
> & { > & {
@ -94,7 +94,7 @@ export const ConversationListItem: FunctionComponent<Props> = React.memo(
theme, theme,
title, title,
type, type,
typingContact, typingContactId,
unblurredAvatarPath, unblurredAvatarPath,
unreadCount, unreadCount,
}) { }) {
@ -121,7 +121,7 @@ export const ConversationListItem: FunctionComponent<Props> = React.memo(
{i18n('ConversationListItem--message-request')} {i18n('ConversationListItem--message-request')}
</span> </span>
); );
} else if (typingContact) { } else if (typingContactId) {
messageText = <TypingAnimation i18n={i18n} />; messageText = <TypingAnimation i18n={i18n} />;
} else if (shouldShowDraft && draftPreview) { } else if (shouldShowDraft && draftPreview) {
messageText = ( messageText = (

View file

@ -1400,9 +1400,6 @@ export class ConversationModel extends window.Backbone
const typingMostRecent = window._.first( const typingMostRecent = window._.first(
window._.sortBy(typingValues, 'timestamp') window._.sortBy(typingValues, 'timestamp')
); );
const typingContact = typingMostRecent
? window.ConversationController.get(typingMostRecent.senderId)
: null;
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const timestamp = this.get('timestamp')!; const timestamp = this.get('timestamp')!;
@ -1440,7 +1437,7 @@ export class ConversationModel extends window.Backbone
// TODO: DESKTOP-720 // TODO: DESKTOP-720
/* eslint-disable @typescript-eslint/no-non-null-assertion */ /* eslint-disable @typescript-eslint/no-non-null-assertion */
const result: ConversationType = { return {
id: this.id, id: this.id,
uuid: this.get('uuid'), uuid: this.get('uuid'),
e164: this.get('e164'), e164: this.get('e164'),
@ -1521,6 +1518,7 @@ export class ConversationModel extends window.Backbone
sortedGroupMembers, sortedGroupMembers,
timestamp, timestamp,
title: this.getTitle()!, title: this.getTitle()!,
typingContactId: typingMostRecent?.senderId,
searchableTitle: isMe(this.attributes) searchableTitle: isMe(this.attributes)
? window.i18n('noteToSelf') ? window.i18n('noteToSelf')
: this.getTitle(), : this.getTitle(),
@ -1537,18 +1535,7 @@ export class ConversationModel extends window.Backbone
sharedGroupNames: [], sharedGroupNames: [],
}), }),
}; };
if (typingContact) {
// We don't want to call .format() on our own conversation
if (typingContact.id === this.id) {
result.typingContact = result;
} else {
result.typingContact = typingContact.format();
}
}
/* eslint-enable @typescript-eslint/no-non-null-assertion */ /* eslint-enable @typescript-eslint/no-non-null-assertion */
return result;
} }
updateE164(e164?: string | null): void { updateE164(e164?: string | null): void {

View file

@ -181,17 +181,7 @@ export type ConversationType = {
unreadCount?: number; unreadCount?: number;
isSelected?: boolean; isSelected?: boolean;
isFetchingUUID?: boolean; isFetchingUUID?: boolean;
typingContact?: { typingContactId?: string;
acceptedMessageRequest: boolean;
avatarPath?: string;
color?: AvatarColorType;
isMe: boolean;
name?: string;
phoneNumber?: string;
profileName?: string;
sharedGroupNames: Array<string>;
title: string;
} | null;
recentMediaItems?: Array<MediaItemType>; recentMediaItems?: Array<MediaItemType>;
profileSharing?: boolean; profileSharing?: boolean;

View file

@ -298,7 +298,7 @@ const mapStateToProps = (state: StateType, props: ExternalProps) => {
...pick(conversation, [ ...pick(conversation, [
'areWeAdmin', 'areWeAdmin',
'unreadCount', 'unreadCount',
'typingContact', 'typingContactId',
'isGroupV1AndDisabled', 'isGroupV1AndDisabled',
]), ]),
isIncomingMessageRequest: Boolean( isIncomingMessageRequest: Boolean(

View file

@ -1,4 +1,4 @@
// 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 { connect } from 'react-redux'; import { connect } from 'react-redux';
@ -7,8 +7,9 @@ import { TypingBubble } from '../../components/conversation/TypingBubble';
import { strictAssert } from '../../util/assert'; import { strictAssert } from '../../util/assert';
import type { StateType } from '../reducer'; import type { StateType } from '../reducer';
import { getIntl } from '../selectors/user'; import { getIntl, getTheme } from '../selectors/user';
import { getConversationSelector } from '../selectors/conversations'; import { getConversationSelector } from '../selectors/conversations';
import { getPreferredBadgeSelector } from '../selectors/badges';
type ExternalProps = { type ExternalProps = {
id: string; id: string;
@ -17,17 +18,21 @@ type ExternalProps = {
const mapStateToProps = (state: StateType, props: ExternalProps) => { const mapStateToProps = (state: StateType, props: ExternalProps) => {
const { id } = props; const { id } = props;
const conversation = getConversationSelector(state)(id); const conversationSelector = getConversationSelector(state);
const conversation = conversationSelector(id);
if (!conversation) { if (!conversation) {
throw new Error(`Did not find conversation ${id} in state!`); throw new Error(`Did not find conversation ${id} in state!`);
} }
strictAssert(conversation.typingContact, 'Missing typingContact'); strictAssert(conversation.typingContactId, 'Missing typing contact ID');
const typingContact = conversationSelector(conversation.typingContactId);
return { return {
...conversation.typingContact, ...typingContact,
badge: getPreferredBadgeSelector(state)(typingContact.badges),
conversationType: conversation.type, conversationType: conversation.type,
i18n: getIntl(state), i18n: getIntl(state),
theme: getTheme(state),
}; };
}; };

View file

@ -1254,11 +1254,7 @@ describe('both/state/selectors/conversations', () => {
title: 'No timestamp', title: 'No timestamp',
unreadCount: 1, unreadCount: 1,
isSelected: false, isSelected: false,
typingContact: { typingContactId: UUID.generate().toString(),
...getDefaultConversation(),
name: 'Someone There',
phoneNumber: '+18005551111',
},
acceptedMessageRequest: true, acceptedMessageRequest: true,
}), }),
@ -1279,11 +1275,7 @@ describe('both/state/selectors/conversations', () => {
title: 'B', title: 'B',
unreadCount: 1, unreadCount: 1,
isSelected: false, isSelected: false,
typingContact: { typingContactId: UUID.generate().toString(),
...getDefaultConversation(),
name: 'Someone There',
phoneNumber: '+18005551111',
},
acceptedMessageRequest: true, acceptedMessageRequest: true,
}), }),
@ -1304,11 +1296,7 @@ describe('both/state/selectors/conversations', () => {
title: 'C', title: 'C',
unreadCount: 1, unreadCount: 1,
isSelected: false, isSelected: false,
typingContact: { typingContactId: UUID.generate().toString(),
...getDefaultConversation(),
name: 'Someone There',
phoneNumber: '+18005551111',
},
acceptedMessageRequest: true, acceptedMessageRequest: true,
}), }),
@ -1329,11 +1317,7 @@ describe('both/state/selectors/conversations', () => {
title: 'A', title: 'A',
unreadCount: 1, unreadCount: 1,
isSelected: false, isSelected: false,
typingContact: { typingContactId: UUID.generate().toString(),
...getDefaultConversation(),
name: 'Someone There',
phoneNumber: '+18005551111',
},
acceptedMessageRequest: true, acceptedMessageRequest: true,
}), }),
@ -1354,11 +1338,7 @@ describe('both/state/selectors/conversations', () => {
title: 'First!', title: 'First!',
unreadCount: 1, unreadCount: 1,
isSelected: false, isSelected: false,
typingContact: { typingContactId: UUID.generate().toString(),
...getDefaultConversation(),
name: 'Someone There',
phoneNumber: '+18005551111',
},
acceptedMessageRequest: true, acceptedMessageRequest: true,
}), }),
@ -1400,11 +1380,7 @@ describe('both/state/selectors/conversations', () => {
title: 'Pin Two', title: 'Pin Two',
unreadCount: 1, unreadCount: 1,
isSelected: false, isSelected: false,
typingContact: { typingContactId: UUID.generate().toString(),
...getDefaultConversation(),
name: 'Someone There',
phoneNumber: '+18005551111',
},
acceptedMessageRequest: true, acceptedMessageRequest: true,
}), }),
@ -1426,11 +1402,7 @@ describe('both/state/selectors/conversations', () => {
title: 'Pin Three', title: 'Pin Three',
unreadCount: 1, unreadCount: 1,
isSelected: false, isSelected: false,
typingContact: { typingContactId: UUID.generate().toString(),
...getDefaultConversation(),
name: 'Someone There',
phoneNumber: '+18005551111',
},
acceptedMessageRequest: true, acceptedMessageRequest: true,
}), }),
@ -1452,11 +1424,7 @@ describe('both/state/selectors/conversations', () => {
title: 'Pin One', title: 'Pin One',
unreadCount: 1, unreadCount: 1,
isSelected: false, isSelected: false,
typingContact: { typingContactId: UUID.generate().toString(),
...getDefaultConversation(),
name: 'Someone There',
phoneNumber: '+18005551111',
},
acceptedMessageRequest: true, acceptedMessageRequest: true,
}), }),
@ -1495,11 +1463,7 @@ describe('both/state/selectors/conversations', () => {
title: 'Pin Two', title: 'Pin Two',
unreadCount: 1, unreadCount: 1,
isSelected: false, isSelected: false,
typingContact: { typingContactId: UUID.generate().toString(),
...getDefaultConversation(),
name: 'Someone There',
phoneNumber: '+18005551111',
},
acceptedMessageRequest: true, acceptedMessageRequest: true,
}), }),
@ -1520,11 +1484,7 @@ describe('both/state/selectors/conversations', () => {
title: 'Pin Three', title: 'Pin Three',
unreadCount: 1, unreadCount: 1,
isSelected: false, isSelected: false,
typingContact: { typingContactId: UUID.generate().toString(),
...getDefaultConversation(),
name: 'Someone There',
phoneNumber: '+18005551111',
},
acceptedMessageRequest: true, acceptedMessageRequest: true,
}), }),
@ -1545,11 +1505,7 @@ describe('both/state/selectors/conversations', () => {
title: 'Pin One', title: 'Pin One',
unreadCount: 1, unreadCount: 1,
isSelected: false, isSelected: false,
typingContact: { typingContactId: UUID.generate().toString(),
...getDefaultConversation(),
name: 'Someone There',
phoneNumber: '+18005551111',
},
acceptedMessageRequest: true, acceptedMessageRequest: true,
}), }),
@ -1571,11 +1527,7 @@ describe('both/state/selectors/conversations', () => {
title: 'Pin One', title: 'Pin One',
unreadCount: 1, unreadCount: 1,
isSelected: false, isSelected: false,
typingContact: { typingContactId: UUID.generate().toString(),
...getDefaultConversation(),
name: 'Someone There',
phoneNumber: '+18005551111',
},
acceptedMessageRequest: true, acceptedMessageRequest: true,
}), }),
@ -1596,11 +1548,7 @@ describe('both/state/selectors/conversations', () => {
title: 'Pin One', title: 'Pin One',
unreadCount: 1, unreadCount: 1,
isSelected: false, isSelected: false,
typingContact: { typingContactId: UUID.generate().toString(),
...getDefaultConversation(),
name: 'Someone There',
phoneNumber: '+18005551111',
},
acceptedMessageRequest: true, acceptedMessageRequest: true,
}), }),