From 2cbcd596090539934e08f11accee817cabdc1a2b Mon Sep 17 00:00:00 2001 From: Evan Hahn <69474926+EvanHahn-Signal@users.noreply.github.com> Date: Wed, 17 Nov 2021 15:11:21 -0600 Subject: [PATCH] Add badges to all conversation lists --- ts/components/ConversationList.stories.tsx | 4 ++ ts/components/ConversationList.tsx | 23 ++++----- ts/components/ForwardMessageModal.tsx | 1 + ts/components/LeftPane.stories.tsx | 5 +- ts/components/LeftPane.tsx | 8 +-- .../AddGroupMembersModal.stories.tsx | 1 + .../AddGroupMembersModal.tsx | 4 ++ .../ChooseGroupMembersModal.tsx | 4 ++ .../ConversationDetails.stories.tsx | 2 +- .../ConversationDetails.tsx | 8 +-- ...versationDetailsMembershipList.stories.tsx | 15 +----- .../ConversationDetailsMembershipList.tsx | 9 ++-- .../BaseConversationListItem.tsx | 8 +-- .../conversationList/ContactCheckbox.tsx | 9 +++- .../conversationList/ContactListItem.tsx | 15 +++++- .../MessageSearchResult.stories.tsx | 51 +++++++++++++------ .../conversationList/MessageSearchResult.tsx | 14 ++++- .../leftPane/LeftPaneComposeHelper.tsx | 6 +-- .../LeftPaneSetGroupMetadataHelper.tsx | 6 +-- ts/state/smart/ConversationDetails.tsx | 12 +---- ts/state/smart/LeftPane.tsx | 4 +- ts/state/smart/MessageSearchResult.tsx | 5 +- 22 files changed, 131 insertions(+), 83 deletions(-) diff --git a/ts/components/ConversationList.stories.tsx b/ts/components/ConversationList.stories.tsx index 07f3a55cb8..4fa011fd97 100644 --- a/ts/components/ConversationList.stories.tsx +++ b/ts/components/ConversationList.stories.tsx @@ -17,6 +17,7 @@ import { ContactCheckboxDisabledReason } from './conversationList/ContactCheckbo import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation'; import { setupI18n } from '../util/setupI18n'; import enMessages from '../../_locales/en/messages.json'; +import { ThemeType } from '../types/Util'; import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext'; import { UUID } from '../types/UUID'; @@ -60,6 +61,7 @@ const Wrapper = ({ height: 350, }} rowCount={rows.length} + getPreferredBadge={() => undefined} getRow={(index: number) => rows[index]} shouldRecomputeRowHeights={false} i18n={i18n} @@ -72,11 +74,13 @@ const Wrapper = ({ bodyRanges={[]} conversationId="marc-convo" from={defaultConversations[0]} + getPreferredBadge={() => undefined} i18n={i18n} id={id} openConversationInternal={action('openConversationInternal')} sentAt={1587358800000} snippet="Lorem <>ipsum<> wow" + theme={ThemeType.light} to={defaultConversations[1]} /> )} diff --git a/ts/components/ConversationList.tsx b/ts/components/ConversationList.tsx index a4ac7cbcba..a85d87ddce 100644 --- a/ts/components/ConversationList.tsx +++ b/ts/components/ConversationList.tsx @@ -8,17 +8,16 @@ import { List } from 'react-virtualized'; import classNames from 'classnames'; import { get, pick } from 'lodash'; -import { getOwn } from '../util/getOwn'; import { missingCaseError } from '../util/missingCaseError'; import { assert } from '../util/assert'; import type { LocalizerType, ThemeType } from '../types/Util'; import { ScrollBehavior } from '../types/Util'; import { getConversationListWidthBreakpoint } from './_util'; -import type { BadgeType } from '../badges/types'; +import type { PreferredBadgeSelectorType } from '../state/selectors/badges'; import type { PropsData as ConversationListItemPropsType } from './conversationList/ConversationListItem'; import { ConversationListItem } from './conversationList/ConversationListItem'; -import type { PropsDataType as ContactListItemPropsType } from './conversationList/ContactListItem'; +import type { ContactListItemConversationType as ContactListItemPropsType } from './conversationList/ContactListItem'; import { ContactListItem } from './conversationList/ContactListItem'; import type { ContactCheckboxDisabledReason } from './conversationList/ContactCheckbox'; import { ContactCheckbox as ContactCheckboxComponent } from './conversationList/ContactCheckbox'; @@ -116,7 +115,6 @@ export type Row = | UsernameRowType; export type PropsType = { - badgesById?: Record; dimensions?: { width: number; height: number; @@ -131,6 +129,7 @@ export type PropsType = { shouldRecomputeRowHeights: boolean; scrollable?: boolean; + getPreferredBadge: PreferredBadgeSelectorType; i18n: LocalizerType; theme: ThemeType; @@ -150,8 +149,8 @@ const NORMAL_ROW_HEIGHT = 76; const HEADER_ROW_HEIGHT = 40; export const ConversationList: React.FC = ({ - badgesById, dimensions, + getPreferredBadge, getRow, i18n, onClickArchiveButton, @@ -231,8 +230,10 @@ export const ConversationList: React.FC = ({ result = ( ); break; @@ -241,10 +242,12 @@ export const ConversationList: React.FC = ({ result = ( ); break; @@ -274,12 +277,6 @@ export const ConversationList: React.FC = ({ 'unreadCount', ]); const { badges, title, unreadCount, lastMessage } = itemProps; - - let badge: undefined | BadgeType; - if (badgesById && badges[0]) { - badge = getOwn(badgesById, badges[0].id); - } - result = (
= ({ = ({ ); }, [ - badgesById, + getPreferredBadge, getRow, i18n, onClickArchiveButton, diff --git a/ts/components/ForwardMessageModal.tsx b/ts/components/ForwardMessageModal.tsx index b68aa46b4a..65bc724275 100644 --- a/ts/components/ForwardMessageModal.tsx +++ b/ts/components/ForwardMessageModal.tsx @@ -398,6 +398,7 @@ export const ForwardMessageModal: FunctionComponent = ({ > = {}): PropsType => ({ - badgesById: {}, cantAddContactToGroup: action('cantAddContactToGroup'), canResizeLeftPane: true, clearGroupCreationError: action('clearGroupCreationError'), @@ -93,6 +93,7 @@ const useProps = (overrideProps: Partial = {}): PropsType => ({ composeReplaceAvatar: action('composeReplaceAvatar'), composeSaveAvatarToDisk: action('composeSaveAvatarToDisk'), createGroup: action('createGroup'), + getPreferredBadge: () => undefined, i18n, modeSpecificProps: defaultModeSpecificProps, preferredWidthFromStorage: 320, @@ -112,11 +113,13 @@ const useProps = (overrideProps: Partial = {}): PropsType => ({ bodyRanges={[]} conversationId="marc-convo" from={defaultConversations[0]} + getPreferredBadge={() => undefined} i18n={i18n} id={id} openConversationInternal={action('openConversationInternal')} sentAt={1587358800000} snippet="Lorem <>ipsum<> wow" + theme={ThemeType.light} to={defaultConversations[1]} /> ), diff --git a/ts/components/LeftPane.tsx b/ts/components/LeftPane.tsx index fd47f1170a..416f8911cd 100644 --- a/ts/components/LeftPane.tsx +++ b/ts/components/LeftPane.tsx @@ -25,7 +25,7 @@ import { LeftPaneSetGroupMetadataHelper } from './leftPane/LeftPaneSetGroupMetad import * as OS from '../OS'; import type { LocalizerType, ThemeType } from '../types/Util'; import { ScrollBehavior } from '../types/Util'; -import type { BadgeType } from '../badges/types'; +import type { PreferredBadgeSelectorType } from '../state/selectors/badges'; import { usePrevious } from '../hooks/usePrevious'; import { missingCaseError } from '../util/missingCaseError'; import { strictAssert } from '../util/assert'; @@ -83,8 +83,8 @@ export type PropsType = { | ({ mode: LeftPaneMode.SetGroupMetadata; } & LeftPaneSetGroupMetadataPropsType); + getPreferredBadge: PreferredBadgeSelectorType; i18n: LocalizerType; - badgesById: Record; preferredWidthFromStorage: number; selectedConversationId: undefined | string; selectedMessageId: undefined | string; @@ -147,7 +147,6 @@ export type PropsType = { }; export const LeftPane: React.FC = ({ - badgesById, cantAddContactToGroup, canResizeLeftPane, challengeStatus, @@ -160,6 +159,7 @@ export const LeftPane: React.FC = ({ composeReplaceAvatar, composeSaveAvatarToDisk, createGroup, + getPreferredBadge, i18n, modeSpecificProps, openConversationInternal, @@ -573,11 +573,11 @@ export const LeftPane: React.FC = ({ tabIndex={-1} > = {}): PropsType => ({ candidateContacts: allCandidateContacts, clearRequestError: action('clearRequestError'), conversationIdsAlreadyInGroup: new Set(), + getPreferredBadge: () => undefined, groupTitle: 'Tahoe Trip', i18n, onClose: action('onClose'), diff --git a/ts/components/conversation/conversation-details/AddGroupMembersModal.tsx b/ts/components/conversation/conversation-details/AddGroupMembersModal.tsx index fb4079434f..3850fc5e89 100644 --- a/ts/components/conversation/conversation-details/AddGroupMembersModal.tsx +++ b/ts/components/conversation/conversation-details/AddGroupMembersModal.tsx @@ -11,6 +11,7 @@ import { AddGroupMemberErrorDialogMode, } from '../../AddGroupMemberErrorDialog'; import type { ConversationType } from '../../../state/ducks/conversations'; +import type { PreferredBadgeSelectorType } from '../../../state/selectors/badges'; import { getGroupSizeRecommendedLimit, getGroupSizeHardLimit, @@ -30,6 +31,7 @@ type PropsType = { candidateContacts: ReadonlyArray; clearRequestError: () => void; conversationIdsAlreadyInGroup: Set; + getPreferredBadge: PreferredBadgeSelectorType; groupTitle: string; i18n: LocalizerType; makeRequest: (conversationIds: ReadonlyArray) => Promise; @@ -147,6 +149,7 @@ export const AddGroupMembersModal: FunctionComponent = ({ candidateContacts, clearRequestError, conversationIdsAlreadyInGroup, + getPreferredBadge, groupTitle, i18n, onClose, @@ -279,6 +282,7 @@ export const AddGroupMembersModal: FunctionComponent = ({ confirmAdds={confirmAdds} contactLookup={contactLookup} conversationIdsAlreadyInGroup={conversationIdsAlreadyInGroup} + getPreferredBadge={getPreferredBadge} i18n={i18n} maxGroupSize={maxGroupSize} onClose={onClose} diff --git a/ts/components/conversation/conversation-details/AddGroupMembersModal/ChooseGroupMembersModal.tsx b/ts/components/conversation/conversation-details/AddGroupMembersModal/ChooseGroupMembersModal.tsx index b2930c8da9..7e435729f6 100644 --- a/ts/components/conversation/conversation-details/AddGroupMembersModal/ChooseGroupMembersModal.tsx +++ b/ts/components/conversation/conversation-details/AddGroupMembersModal/ChooseGroupMembersModal.tsx @@ -14,6 +14,7 @@ import { useRestoreFocus } from '../../../../hooks/useRestoreFocus'; import { missingCaseError } from '../../../../util/missingCaseError'; import { filterAndSortConversationsByTitle } from '../../../../util/filterAndSortConversations'; import type { ConversationType } from '../../../../state/ducks/conversations'; +import type { PreferredBadgeSelectorType } from '../../../../state/selectors/badges'; import { ModalHost } from '../../../ModalHost'; import { ContactPills } from '../../../ContactPills'; import { ContactPill } from '../../../ContactPill'; @@ -28,6 +29,7 @@ type PropsType = { confirmAdds: () => void; contactLookup: Record; conversationIdsAlreadyInGroup: Set; + getPreferredBadge: PreferredBadgeSelectorType; i18n: LocalizerType; maxGroupSize: number; onClose: () => void; @@ -48,6 +50,7 @@ export const ChooseGroupMembersModal: FunctionComponent = ({ confirmAdds, contactLookup, conversationIdsAlreadyInGroup, + getPreferredBadge, i18n, maxGroupSize, onClose, @@ -192,6 +195,7 @@ export const ChooseGroupMembersModal: FunctionComponent = ({ > ({ } : conversation, hasGroupLink, + getPreferredBadge: () => undefined, i18n, isAdmin: false, isGroup: true, @@ -56,7 +57,6 @@ const createProps = (hasGroupLink = false, expireTimer?: number): Props => ({ isMe: i === 2, }), })), - preferredBadgeByConversation: {}, pendingApprovalMemberships: times(8, () => ({ member: getDefaultConversation(), })), diff --git a/ts/components/conversation/conversation-details/ConversationDetails.tsx b/ts/components/conversation/conversation-details/ConversationDetails.tsx index 1dde593c02..104b9cfc7a 100644 --- a/ts/components/conversation/conversation-details/ConversationDetails.tsx +++ b/ts/components/conversation/conversation-details/ConversationDetails.tsx @@ -6,6 +6,7 @@ import React, { useState } from 'react'; import { Button, ButtonIconType, ButtonVariant } from '../../Button'; import type { ConversationType } from '../../../state/ducks/conversations'; +import type { PreferredBadgeSelectorType } from '../../../state/selectors/badges'; import { assert } from '../../../util/assert'; import { getMutedUntilText } from '../../../util/getMutedUntilText'; @@ -59,12 +60,12 @@ export type StateProps = { candidateContactsToAdd: Array; conversation?: ConversationType; hasGroupLink: boolean; + getPreferredBadge: PreferredBadgeSelectorType; i18n: LocalizerType; isAdmin: boolean; isGroup: boolean; loadRecentMediaItems: (limit: number) => void; memberships: Array; - preferredBadgeByConversation: Record; pendingApprovalMemberships: ReadonlyArray; pendingMemberships: ReadonlyArray; setDisappearingMessages: (seconds: number) => void; @@ -114,6 +115,7 @@ export const ConversationDetails: React.ComponentType = ({ conversation, deleteAvatarFromDisk, hasGroupLink, + getPreferredBadge, i18n, isAdmin, isGroup, @@ -126,7 +128,6 @@ export const ConversationDetails: React.ComponentType = ({ onUnblock, pendingApprovalMemberships, pendingMemberships, - preferredBadgeByConversation, replaceAvatar, saveAvatarToDisk, searchInConversation, @@ -235,6 +236,7 @@ export const ConversationDetails: React.ComponentType = ({ conversationIdsAlreadyInGroup={ new Set(memberships.map(membership => membership.member.id)) } + getPreferredBadge={getPreferredBadge} groupTitle={conversation.title} i18n={i18n} makeRequest={async conversationIds => { @@ -459,9 +461,9 @@ export const ConversationDetails: React.ComponentType = ({ { setModalState(ModalState.AddingGroupMembers); diff --git a/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.stories.tsx b/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.stories.tsx index eee64e54de..a18869cab9 100644 --- a/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.stories.tsx +++ b/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.stories.tsx @@ -11,9 +11,7 @@ import { number } from '@storybook/addon-knobs'; import { setupI18n } from '../../../util/setupI18n'; import enMessages from '../../../../_locales/en/messages.json'; import { getDefaultConversation } from '../../../test-both/helpers/getDefaultConversation'; -import { getFakeBadge } from '../../../test-both/helpers/getFakeBadge'; import { ThemeType } from '../../../types/Util'; -import type { BadgeType } from '../../../badges/types'; import type { Props, @@ -48,20 +46,9 @@ const createProps = (overrideProps: Partial): Props => ({ ? overrideProps.canAddNewMembers : false, conversationId: '123', + getPreferredBadge: () => undefined, i18n, memberships: overrideProps.memberships || [], - preferredBadgeByConversation: - overrideProps.preferredBadgeByConversation || - (overrideProps.memberships || []).reduce( - (result: Record, { member }, index) => - (index + 1) % 3 === 0 - ? { - ...result, - [member.id]: getFakeBadge({ alternate: index % 2 !== 0 }), - } - : result, - {} - ), showContactModal: action('showContactModal'), startAddingNewMembers: action('startAddingNewMembers'), theme: ThemeType.light, diff --git a/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.tsx b/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.tsx index 567f47dd50..9f44b63ace 100644 --- a/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.tsx +++ b/ts/components/conversation/conversation-details/ConversationDetailsMembershipList.tsx @@ -4,14 +4,13 @@ import React from 'react'; import type { LocalizerType, ThemeType } from '../../../types/Util'; -import { getOwn } from '../../../util/getOwn'; -import type { BadgeType } from '../../../badges/types'; import { Avatar } from '../../Avatar'; import { Emojify } from '../Emojify'; import { ConversationDetailsIcon, IconType } from './ConversationDetailsIcon'; import type { ConversationType } from '../../../state/ducks/conversations'; +import type { PreferredBadgeSelectorType } from '../../../state/selectors/badges'; import { PanelRow } from './PanelRow'; import { PanelSection } from './PanelSection'; @@ -23,10 +22,10 @@ export type GroupV2Membership = { export type Props = { canAddNewMembers: boolean; conversationId: string; + getPreferredBadge: PreferredBadgeSelectorType; i18n: LocalizerType; maxShownMemberCount?: number; memberships: Array; - preferredBadgeByConversation: Record; showContactModal: (contactId: string, conversationId: string) => void; startAddingNewMembers?: () => void; theme: ThemeType; @@ -74,10 +73,10 @@ function sortMemberships( export const ConversationDetailsMembershipList: React.ComponentType = ({ canAddNewMembers, conversationId, + getPreferredBadge, i18n, maxShownMemberCount = 5, memberships, - preferredBadgeByConversation, showContactModal, startAddingNewMembers, theme, @@ -114,7 +113,7 @@ export const ConversationDetailsMembershipList: React.ComponentType = ({ icon={ void; shouldShowSpinner?: boolean; - theme?: ThemeType; unreadCount?: number; } & Pick< ConversationType, @@ -62,7 +60,11 @@ type PropsType = { | 'sharedGroupNames' | 'title' | 'unblurredAvatarPath' ->; +> & + ( + | { badge?: undefined; theme?: ThemeType } + | { badge: BadgeType; theme: ThemeType } + ); export const BaseConversationListItem: FunctionComponent = React.memo(function BaseConversationListItem({ diff --git a/ts/components/conversationList/ContactCheckbox.tsx b/ts/components/conversationList/ContactCheckbox.tsx index 2967953aa4..f1863a2d56 100644 --- a/ts/components/conversationList/ContactCheckbox.tsx +++ b/ts/components/conversationList/ContactCheckbox.tsx @@ -9,7 +9,8 @@ import { HEADER_CONTACT_NAME_CLASS_NAME, } from './BaseConversationListItem'; import type { ConversationType } from '../../state/ducks/conversations'; -import type { LocalizerType } from '../../types/Util'; +import type { BadgeType } from '../../badges/types'; +import type { LocalizerType, ThemeType } from '../../types/Util'; import { ContactName } from '../conversation/ContactName'; import { About } from '../conversation/About'; @@ -21,6 +22,7 @@ export enum ContactCheckboxDisabledReason { } export type PropsDataType = { + badge: undefined | BadgeType; disabledReason?: ContactCheckboxDisabledReason; isChecked: boolean; } & Pick< @@ -46,6 +48,7 @@ type PropsHousekeepingType = { id: string, disabledReason: undefined | ContactCheckboxDisabledReason ) => void; + theme: ThemeType; }; type PropsType = PropsDataType & PropsHousekeepingType; @@ -55,6 +58,7 @@ export const ContactCheckbox: FunctionComponent = React.memo( about, acceptedMessageRequest, avatarPath, + badge, color, disabledReason, i18n, @@ -66,6 +70,7 @@ export const ContactCheckbox: FunctionComponent = React.memo( phoneNumber, profileName, sharedGroupNames, + theme, title, type, unblurredAvatarPath, @@ -97,6 +102,7 @@ export const ContactCheckbox: FunctionComponent = React.memo( = React.memo( phoneNumber={phoneNumber} profileName={profileName} sharedGroupNames={sharedGroupNames} + theme={theme} title={title} unblurredAvatarPath={unblurredAvatarPath} /> diff --git a/ts/components/conversationList/ContactListItem.tsx b/ts/components/conversationList/ContactListItem.tsx index 92dd1e2d19..4d5226098f 100644 --- a/ts/components/conversationList/ContactListItem.tsx +++ b/ts/components/conversationList/ContactListItem.tsx @@ -9,15 +9,17 @@ import { HEADER_CONTACT_NAME_CLASS_NAME, } from './BaseConversationListItem'; import type { ConversationType } from '../../state/ducks/conversations'; -import type { LocalizerType } from '../../types/Util'; +import type { BadgeType } from '../../badges/types'; +import type { LocalizerType, ThemeType } from '../../types/Util'; import { ContactName } from '../conversation/ContactName'; import { About } from '../conversation/About'; -export type PropsDataType = Pick< +export type ContactListItemConversationType = Pick< ConversationType, | 'about' | 'acceptedMessageRequest' | 'avatarPath' + | 'badges' | 'color' | 'id' | 'isMe' @@ -30,9 +32,14 @@ export type PropsDataType = Pick< | 'unblurredAvatarPath' >; +type PropsDataType = ContactListItemConversationType & { + badge: undefined | BadgeType; +}; + type PropsHousekeepingType = { i18n: LocalizerType; onClick?: (id: string) => void; + theme: ThemeType; }; type PropsType = PropsDataType & PropsHousekeepingType; @@ -42,6 +49,7 @@ export const ContactListItem: FunctionComponent = React.memo( about, acceptedMessageRequest, avatarPath, + badge, color, i18n, id, @@ -51,6 +59,7 @@ export const ContactListItem: FunctionComponent = React.memo( phoneNumber, profileName, sharedGroupNames, + theme, title, type, unblurredAvatarPath, @@ -70,6 +79,7 @@ export const ContactListItem: FunctionComponent = React.memo( = React.memo( phoneNumber={phoneNumber} profileName={profileName} sharedGroupNames={sharedGroupNames} + theme={theme} title={title} unblurredAvatarPath={unblurredAvatarPath} /> diff --git a/ts/components/conversationList/MessageSearchResult.stories.tsx b/ts/components/conversationList/MessageSearchResult.stories.tsx index 841d48deb2..cce9ca89d2 100644 --- a/ts/components/conversationList/MessageSearchResult.stories.tsx +++ b/ts/components/conversationList/MessageSearchResult.stories.tsx @@ -8,6 +8,9 @@ import { boolean, text, withKnobs } from '@storybook/addon-knobs'; import { setupI18n } from '../../util/setupI18n'; import enMessages from '../../../_locales/en/messages.json'; +import { StorybookThemeContext } from '../../../.storybook/StorybookThemeContext'; +import { strictAssert } from '../../util/assert'; +import { getFakeBadge } from '../../test-both/helpers/getFakeBadge'; import type { PropsType } from './MessageSearchResult'; import { MessageSearchResult } from './MessageSearchResult'; import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation'; @@ -37,7 +40,7 @@ const group = getDefaultConversation({ type: 'group', }); -const createProps = (overrideProps: Partial = {}): PropsType => ({ +const useProps = (overrideProps: Partial = {}): PropsType => ({ i18n, id: '', conversationId: '', @@ -50,16 +53,18 @@ const createProps = (overrideProps: Partial = {}): PropsType => ({ bodyRanges: overrideProps.bodyRanges || [], from: overrideProps.from as PropsType['from'], to: overrideProps.to as PropsType['to'], + getPreferredBadge: overrideProps.getPreferredBadge || (() => undefined), isSelected: boolean('isSelected', overrideProps.isSelected || false), openConversationInternal: action('openConversationInternal'), isSearchingInConversation: boolean( 'isSearchingInConversation', overrideProps.isSearchingInConversation || false ), + theme: React.useContext(StorybookThemeContext), }); story.add('Default', () => { - const props = createProps({ + const props = useProps({ from: someone, to: me, }); @@ -67,8 +72,24 @@ story.add('Default', () => { return ; }); +story.add('Sender has a badge', () => { + const props = useProps({ + from: { ...someone, badges: [{ id: 'sender badge' }] }, + to: me, + getPreferredBadge: badges => { + strictAssert( + badges[0]?.id === 'sender badge', + 'Rendering the wrong badge!' + ); + return getFakeBadge(); + }, + }); + + return ; +}); + story.add('Selected', () => { - const props = createProps({ + const props = useProps({ from: someone, to: me, isSelected: true, @@ -78,7 +99,7 @@ story.add('Selected', () => { }); story.add('From You', () => { - const props = createProps({ + const props = useProps({ from: me, to: someone, }); @@ -87,7 +108,7 @@ story.add('From You', () => { }); story.add('Searching in Conversation', () => { - const props = createProps({ + const props = useProps({ from: me, to: someone, isSearchingInConversation: true, @@ -97,7 +118,7 @@ story.add('Searching in Conversation', () => { }); story.add('From You to Yourself', () => { - const props = createProps({ + const props = useProps({ from: me, to: me, }); @@ -106,7 +127,7 @@ story.add('From You to Yourself', () => { }); story.add('From You to Group', () => { - const props = createProps({ + const props = useProps({ from: me, to: group, }); @@ -115,7 +136,7 @@ story.add('From You to Group', () => { }); story.add('From Someone to Group', () => { - const props = createProps({ + const props = useProps({ from: someone, to: group, }); @@ -130,7 +151,7 @@ story.add('Long Search Result', () => { ]; return snippets.map(snippet => { - const props = createProps({ + const props = useProps({ from: someone, to: me, snippet, @@ -141,13 +162,13 @@ story.add('Long Search Result', () => { }); story.add('Empty (should be invalid)', () => { - const props = createProps(); + const props = useProps(); return ; }); story.add('@mention', () => { - const props = createProps({ + const props = useProps({ body: 'moss banana twine sound lake zoo brain count vacuum work stairs try power forget hair dry diary years no results \uFFFC elephant sorry umbrella potato igloo kangaroo home Georgia bayonet vector orange forge diary zebra turtle rise front \uFFFC', bodyRanges: [ { @@ -173,7 +194,7 @@ story.add('@mention', () => { }); story.add('@mention regexp', () => { - const props = createProps({ + const props = useProps({ body: '\uFFFC This is a (long) /text/ ^$ that is ... specially **crafted** to (test) our regexp escaping mechanism! Making sure that the code we write works in all sorts of scenarios', bodyRanges: [ { @@ -193,7 +214,7 @@ story.add('@mention regexp', () => { }); story.add('@mention no-matches', () => { - const props = createProps({ + const props = useProps({ body: '\uFFFC hello', bodyRanges: [ { @@ -212,7 +233,7 @@ story.add('@mention no-matches', () => { }); story.add('@mention no-matches', () => { - const props = createProps({ + const props = useProps({ body: 'moss banana twine sound lake zoo brain count vacuum work stairs try power forget hair dry diary years no results \uFFFC elephant sorry umbrella potato igloo kangaroo home Georgia bayonet vector orange forge diary zebra turtle rise front \uFFFC', bodyRanges: [ { @@ -238,7 +259,7 @@ story.add('@mention no-matches', () => { }); story.add('Double @mention', () => { - const props = createProps({ + const props = useProps({ body: 'Hey \uFFFC \uFFFC test', bodyRanges: [ { diff --git a/ts/components/conversationList/MessageSearchResult.tsx b/ts/components/conversationList/MessageSearchResult.tsx index 3c45a7e4cc..205403799c 100644 --- a/ts/components/conversationList/MessageSearchResult.tsx +++ b/ts/components/conversationList/MessageSearchResult.tsx @@ -9,9 +9,14 @@ import { MessageBodyHighlight } from './MessageBodyHighlight'; import { ContactName } from '../conversation/ContactName'; import { assert } from '../../util/assert'; -import type { BodyRangesType, LocalizerType } from '../../types/Util'; +import type { + BodyRangesType, + LocalizerType, + ThemeType, +} from '../../types/Util'; import { BaseConversationListItem } from './BaseConversationListItem'; import type { ConversationType } from '../../state/ducks/conversations'; +import type { PreferredBadgeSelectorType } from '../../state/selectors/badges'; export type PropsDataType = { isSelected?: boolean; @@ -29,6 +34,7 @@ export type PropsDataType = { ConversationType, | 'acceptedMessageRequest' | 'avatarPath' + | 'badges' | 'color' | 'isMe' | 'name' @@ -50,11 +56,13 @@ export type PropsDataType = { }; type PropsHousekeepingType = { + getPreferredBadge: PreferredBadgeSelectorType; i18n: LocalizerType; openConversationInternal: (_: { conversationId: string; messageId?: string; }) => void; + theme: ThemeType; }; export type PropsType = PropsDataType & PropsHousekeepingType; @@ -136,11 +144,13 @@ export const MessageSearchResult: FunctionComponent = React.memo( bodyRanges, conversationId, from, + getPreferredBadge, i18n, id, openConversationInternal, sentAt, snippet, + theme, to, }) { const onClickItem = useCallback(() => { @@ -179,6 +189,7 @@ export const MessageSearchResult: FunctionComponent = React.memo( = React.memo( phoneNumber={from.phoneNumber} profileName={from.profileName} sharedGroupNames={from.sharedGroupNames} + theme={theme} title={from.title} unblurredAvatarPath={from.unblurredAvatarPath} /> diff --git a/ts/components/leftPane/LeftPaneComposeHelper.tsx b/ts/components/leftPane/LeftPaneComposeHelper.tsx index a9b761b9e6..5adb825bea 100644 --- a/ts/components/leftPane/LeftPaneComposeHelper.tsx +++ b/ts/components/leftPane/LeftPaneComposeHelper.tsx @@ -8,7 +8,7 @@ import type { PhoneNumber } from 'google-libphonenumber'; import { LeftPaneHelper } from './LeftPaneHelper'; import type { Row } from '../ConversationList'; import { RowType } from '../ConversationList'; -import type { PropsDataType as ContactListItemPropsType } from '../conversationList/ContactListItem'; +import type { ContactListItemConversationType } from '../conversationList/ContactListItem'; import type { PropsData as ConversationListItemPropsType } from '../conversationList/ConversationListItem'; import { SearchInput } from '../SearchInput'; import type { LocalizerType } from '../../types/Util'; @@ -21,7 +21,7 @@ import { missingCaseError } from '../../util/missingCaseError'; import { getUsernameFromSearch } from '../../types/Username'; export type LeftPaneComposePropsType = { - composeContacts: ReadonlyArray; + composeContacts: ReadonlyArray; composeGroups: ReadonlyArray; regionCode: string; @@ -37,7 +37,7 @@ enum TopButton { } export class LeftPaneComposeHelper extends LeftPaneHelper { - private readonly composeContacts: ReadonlyArray; + private readonly composeContacts: ReadonlyArray; private readonly composeGroups: ReadonlyArray; diff --git a/ts/components/leftPane/LeftPaneSetGroupMetadataHelper.tsx b/ts/components/leftPane/LeftPaneSetGroupMetadataHelper.tsx index 86cb482e0d..59fca321d7 100644 --- a/ts/components/leftPane/LeftPaneSetGroupMetadataHelper.tsx +++ b/ts/components/leftPane/LeftPaneSetGroupMetadataHelper.tsx @@ -7,7 +7,7 @@ import React from 'react'; import { LeftPaneHelper } from './LeftPaneHelper'; import type { Row } from '../ConversationList'; import { RowType } from '../ConversationList'; -import type { PropsDataType as ContactListItemPropsType } from '../conversationList/ContactListItem'; +import type { ContactListItemConversationType } from '../conversationList/ContactListItem'; import { DisappearingTimerSelect } from '../DisappearingTimerSelect'; import type { LocalizerType } from '../../types/Util'; import { Alert } from '../Alert'; @@ -32,7 +32,7 @@ export type LeftPaneSetGroupMetadataPropsType = { hasError: boolean; isCreating: boolean; isEditingAvatar: boolean; - selectedContacts: ReadonlyArray; + selectedContacts: ReadonlyArray; userAvatarData: ReadonlyArray; }; @@ -49,7 +49,7 @@ export class LeftPaneSetGroupMetadataHelper extends LeftPaneHelper; + private readonly selectedContacts: ReadonlyArray; private readonly userAvatarData: ReadonlyArray; diff --git a/ts/state/smart/ConversationDetails.tsx b/ts/state/smart/ConversationDetails.tsx index c090f6e5a0..c4badd23f3 100644 --- a/ts/state/smart/ConversationDetails.tsx +++ b/ts/state/smart/ConversationDetails.tsx @@ -19,7 +19,6 @@ import { getBadgesSelector, getPreferredBadgeSelector, } from '../selectors/badges'; -import type { BadgeType } from '../../badges/types'; import { assert } from '../../util/assert'; import { SignalService as Proto } from '../../protobuf'; @@ -81,24 +80,15 @@ const mapStateToProps = ( const badges = getBadgesSelector(state)(conversation.badges); - const preferredBadgeByConversation: Record = {}; - const getPreferredBadge = getPreferredBadgeSelector(state); - groupMemberships.memberships.forEach(({ member }) => { - const preferredBadge = getPreferredBadge(member.badges); - if (preferredBadge) { - preferredBadgeByConversation[member.id] = preferredBadge; - } - }); - return { ...props, badges, canEditGroupInfo, candidateContactsToAdd, conversation, + getPreferredBadge: getPreferredBadgeSelector(state), i18n: getIntl(state), isAdmin, - preferredBadgeByConversation, ...groupMemberships, userAvatarData: conversation.avatars || [], hasGroupLink, diff --git a/ts/state/smart/LeftPane.tsx b/ts/state/smart/LeftPane.tsx index 6082b3fb37..ca7753870b 100644 --- a/ts/state/smart/LeftPane.tsx +++ b/ts/state/smart/LeftPane.tsx @@ -20,7 +20,7 @@ import { isSearching, } from '../selectors/search'; import { getIntl, getRegionCode, getTheme } from '../selectors/user'; -import { getBadgesById } from '../selectors/badges'; +import { getPreferredBadgeSelector } from '../selectors/badges'; import { getPreferredLeftPaneWidth, getUsernamesEnabled, @@ -166,7 +166,6 @@ const getModeSpecificProps = ( const mapStateToProps = (state: StateType) => { return { modeSpecificProps: getModeSpecificProps(state), - badgesById: getBadgesById(state), canResizeLeftPane: window.Signal.RemoteConfig.isEnabled( 'desktop.internalUser' ), @@ -174,6 +173,7 @@ const mapStateToProps = (state: StateType) => { selectedConversationId: getSelectedConversationId(state), selectedMessageId: getSelectedMessage(state)?.id, showArchived: getShowArchived(state), + getPreferredBadge: getPreferredBadgeSelector(state), i18n: getIntl(state), regionCode: getRegionCode(state), challengeStatus: state.network.challengeStatus, diff --git a/ts/state/smart/MessageSearchResult.tsx b/ts/state/smart/MessageSearchResult.tsx index 3d78ff8f9c..34772a7239 100644 --- a/ts/state/smart/MessageSearchResult.tsx +++ b/ts/state/smart/MessageSearchResult.tsx @@ -8,7 +8,8 @@ import { mapDispatchToProps } from '../actions'; import type { StateType } from '../reducer'; import { MessageSearchResult } from '../../components/conversationList/MessageSearchResult'; -import { getIntl } from '../selectors/user'; +import { getPreferredBadgeSelector } from '../selectors/badges'; +import { getIntl, getTheme } from '../selectors/user'; import { getMessageSearchResultSelector } from '../selectors/search'; type SmartProps = { @@ -26,8 +27,10 @@ function mapStateToProps(state: StateType, ourProps: SmartProps) { return { ...props, + getPreferredBadge: getPreferredBadgeSelector(state), i18n: getIntl(state), style, + theme: getTheme(state), }; } const smart = connect(mapStateToProps, mapDispatchToProps);