Show badges on message avatars and on message details screen
This commit is contained in:
parent
dc1260f9cd
commit
ca7fb47958
|
@ -152,7 +152,7 @@
|
|||
"redux-promise-middleware": "6.1.0",
|
||||
"redux-thunk": "2.3.0",
|
||||
"redux-ts-utils": "3.2.2",
|
||||
"reselect": "4.0.0",
|
||||
"reselect": "4.1.2",
|
||||
"rimraf": "2.6.2",
|
||||
"ringrtc": "https://github.com/signalapp/signal-ringrtc-node.git#f8764156753e68eb85220dc1c04a6ced3704bedf",
|
||||
"rotating-file-stream": "2.1.5",
|
||||
|
|
|
@ -31,6 +31,8 @@ import { getDefaultConversation } from '../../test-both/helpers/getDefaultConver
|
|||
import { WidthBreakpoint } from '../_util';
|
||||
|
||||
import { fakeAttachment } from '../../test-both/helpers/fakeAttachment';
|
||||
import { getFakeBadge } from '../../test-both/helpers/getFakeBadge';
|
||||
import { ThemeType } from '../../types/Util';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
|
@ -95,6 +97,7 @@ const renderAudioAttachment: Props['renderAudioAttachment'] = props => (
|
|||
const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
||||
attachments: overrideProps.attachments,
|
||||
author: overrideProps.author || getDefaultConversation(),
|
||||
authorBadge: overrideProps.authorBadge,
|
||||
reducedMotion: boolean('reducedMotion', false),
|
||||
bodyRanges: overrideProps.bodyRanges,
|
||||
canReply: true,
|
||||
|
@ -176,6 +179,7 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
status: overrideProps.status || 'sent',
|
||||
text: overrideProps.text || text('text', ''),
|
||||
textPending: boolean('textPending', overrideProps.textPending || false),
|
||||
theme: ThemeType.light,
|
||||
timestamp: number('timestamp', overrideProps.timestamp || Date.now()),
|
||||
});
|
||||
|
||||
|
@ -544,6 +548,17 @@ story.add('Avatar in Group', () => {
|
|||
return <Message {...props} />;
|
||||
});
|
||||
|
||||
story.add('Badge in Group', () => {
|
||||
const props = createProps({
|
||||
authorBadge: getFakeBadge(),
|
||||
conversationType: 'group',
|
||||
status: 'sent',
|
||||
text: 'Hello it is me, the saxophone.',
|
||||
});
|
||||
|
||||
return <Message {...props} />;
|
||||
});
|
||||
|
||||
story.add('Sticker', () => {
|
||||
const props = createProps({
|
||||
attachments: [
|
||||
|
|
|
@ -62,6 +62,7 @@ import type {
|
|||
LocalizerType,
|
||||
ThemeType,
|
||||
} from '../../types/Util';
|
||||
import type { BadgeType } from '../../badges/types';
|
||||
import type {
|
||||
ContactNameColorType,
|
||||
ConversationColorType,
|
||||
|
@ -145,6 +146,7 @@ export type PropsData = {
|
|||
ConversationType,
|
||||
| 'acceptedMessageRequest'
|
||||
| 'avatarPath'
|
||||
| 'badges'
|
||||
| 'color'
|
||||
| 'id'
|
||||
| 'isMe'
|
||||
|
@ -155,6 +157,7 @@ export type PropsData = {
|
|||
| 'title'
|
||||
| 'unblurredAvatarPath'
|
||||
>;
|
||||
authorBadge: undefined | BadgeType;
|
||||
reducedMotion?: boolean;
|
||||
conversationType: ConversationTypeType;
|
||||
attachments?: Array<AttachmentType>;
|
||||
|
@ -203,7 +206,7 @@ export type PropsHousekeeping = {
|
|||
containerWidthBreakpoint: WidthBreakpoint;
|
||||
i18n: LocalizerType;
|
||||
interactionMode: InteractionModeType;
|
||||
theme?: ThemeType;
|
||||
theme: ThemeType;
|
||||
disableMenu?: boolean;
|
||||
disableScroll?: boolean;
|
||||
collapseMetadata?: boolean;
|
||||
|
@ -1187,7 +1190,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
}
|
||||
|
||||
public renderAvatar(): JSX.Element | undefined {
|
||||
const { author, i18n, showContactModal } = this.props;
|
||||
const { author, authorBadge, i18n, showContactModal, theme } = this.props;
|
||||
|
||||
if (!this.hasAvatar()) {
|
||||
return undefined;
|
||||
|
@ -1202,6 +1205,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
<Avatar
|
||||
acceptedMessageRequest={author.acceptedMessageRequest}
|
||||
avatarPath={author.avatarPath}
|
||||
badge={authorBadge}
|
||||
color={author.color}
|
||||
conversationType="direct"
|
||||
i18n={i18n}
|
||||
|
@ -1217,6 +1221,7 @@ export class Message extends React.PureComponent<Props, State> {
|
|||
profileName={author.profileName}
|
||||
sharedGroupNames={author.sharedGroupNames}
|
||||
size={28}
|
||||
theme={theme}
|
||||
title={author.title}
|
||||
unblurredAvatarPath={author.unblurredAvatarPath}
|
||||
/>
|
||||
|
|
|
@ -15,6 +15,8 @@ import { ReadStatus } from '../../messages/MessageReadStatus';
|
|||
import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation';
|
||||
import { setupI18n } from '../../util/setupI18n';
|
||||
import enMessages from '../../../_locales/en/messages.json';
|
||||
import { getFakeBadge } from '../../test-both/helpers/getFakeBadge';
|
||||
import { ThemeType } from '../../types/Util';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
|
@ -25,6 +27,7 @@ const defaultMessage: MessageDataPropsType = {
|
|||
id: 'some-id',
|
||||
title: 'Max',
|
||||
}),
|
||||
authorBadge: getFakeBadge(),
|
||||
canReply: true,
|
||||
canDeleteForEveryone: true,
|
||||
canDownload: true,
|
||||
|
@ -59,8 +62,10 @@ const createProps = (overrideProps: Partial<Props> = {}): Props => ({
|
|||
receivedAt: number('receivedAt', overrideProps.receivedAt || Date.now()),
|
||||
sentAt: number('sentAt', overrideProps.sentAt || Date.now()),
|
||||
|
||||
getPreferredBadge: () => getFakeBadge(),
|
||||
i18n,
|
||||
interactionMode: 'keyboard',
|
||||
theme: ThemeType.light,
|
||||
|
||||
showSafetyNumber: action('showSafetyNumber'),
|
||||
|
||||
|
|
|
@ -14,8 +14,9 @@ import type {
|
|||
PropsData as MessagePropsDataType,
|
||||
} from './Message';
|
||||
import { Message } from './Message';
|
||||
import type { LocalizerType } from '../../types/Util';
|
||||
import type { LocalizerType, ThemeType } from '../../types/Util';
|
||||
import type { ConversationType } from '../../state/ducks/conversations';
|
||||
import type { PreferredBadgeSelectorType } from '../../state/selectors/badges';
|
||||
import { groupBy } from '../../util/mapUtil';
|
||||
import type { ContactNameColorType } from '../../types/Colors';
|
||||
import { SendStatus } from '../../messages/MessageSendState';
|
||||
|
@ -27,6 +28,7 @@ export type Contact = Pick<
|
|||
ConversationType,
|
||||
| 'acceptedMessageRequest'
|
||||
| 'avatarPath'
|
||||
| 'badges'
|
||||
| 'color'
|
||||
| 'id'
|
||||
| 'isMe'
|
||||
|
@ -60,6 +62,8 @@ export type PropsData = {
|
|||
|
||||
showSafetyNumber: (contactId: string) => void;
|
||||
i18n: LocalizerType;
|
||||
theme: ThemeType;
|
||||
getPreferredBadge: PreferredBadgeSelectorType;
|
||||
} & Pick<MessagePropsType, 'interactionMode'>;
|
||||
|
||||
export type PropsBackboneActions = Pick<
|
||||
|
@ -116,10 +120,11 @@ export class MessageDetail extends React.Component<Props> {
|
|||
}
|
||||
|
||||
public renderAvatar(contact: Contact): JSX.Element {
|
||||
const { i18n } = this.props;
|
||||
const { getPreferredBadge, i18n, theme } = this.props;
|
||||
const {
|
||||
acceptedMessageRequest,
|
||||
avatarPath,
|
||||
badges,
|
||||
color,
|
||||
isMe,
|
||||
name,
|
||||
|
@ -134,6 +139,7 @@ export class MessageDetail extends React.Component<Props> {
|
|||
<Avatar
|
||||
acceptedMessageRequest={acceptedMessageRequest}
|
||||
avatarPath={avatarPath}
|
||||
badge={getPreferredBadge(badges)}
|
||||
color={color}
|
||||
conversationType="direct"
|
||||
i18n={i18n}
|
||||
|
@ -141,6 +147,7 @@ export class MessageDetail extends React.Component<Props> {
|
|||
name={name}
|
||||
phoneNumber={phoneNumber}
|
||||
profileName={profileName}
|
||||
theme={theme}
|
||||
title={title}
|
||||
sharedGroupNames={sharedGroupNames}
|
||||
size={AvatarSize.THIRTY_SIX}
|
||||
|
@ -288,6 +295,7 @@ export class MessageDetail extends React.Component<Props> {
|
|||
showExpiredOutgoingTapToViewToast,
|
||||
showForwardMessageModal,
|
||||
showVisualAttachment,
|
||||
theme,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
|
@ -350,6 +358,7 @@ export class MessageDetail extends React.Component<Props> {
|
|||
log.warn('MessageDetail: deleteMessageForEveryone called!');
|
||||
}}
|
||||
showVisualAttachment={showVisualAttachment}
|
||||
theme={theme}
|
||||
/>
|
||||
</div>
|
||||
<table className="module-message-detail__info">
|
||||
|
|
|
@ -26,6 +26,7 @@ import { setupI18n } from '../../util/setupI18n';
|
|||
import enMessages from '../../../_locales/en/messages.json';
|
||||
import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation';
|
||||
import { WidthBreakpoint } from '../_util';
|
||||
import { ThemeType } from '../../types/Util';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
|
@ -36,6 +37,7 @@ const defaultMessageProps: MessagesProps = {
|
|||
id: 'some-id',
|
||||
title: 'Person X',
|
||||
}),
|
||||
authorBadge: undefined,
|
||||
canReply: true,
|
||||
canDeleteForEveryone: true,
|
||||
canDownload: true,
|
||||
|
@ -90,6 +92,7 @@ const defaultMessageProps: MessagesProps = {
|
|||
showVisualAttachment: action('default--showVisualAttachment'),
|
||||
status: 'sent',
|
||||
text: 'This is really interesting.',
|
||||
theme: ThemeType.light,
|
||||
timestamp: Date.now(),
|
||||
};
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ const items: Record<string, TimelineItemType> = {
|
|||
author: getDefaultConversation({
|
||||
phoneNumber: '(202) 555-2001',
|
||||
}),
|
||||
authorBadge: undefined,
|
||||
canDeleteForEveryone: false,
|
||||
canDownload: true,
|
||||
canReply: true,
|
||||
|
@ -66,6 +67,7 @@ const items: Record<string, TimelineItemType> = {
|
|||
type: 'message',
|
||||
data: {
|
||||
author: getDefaultConversation({}),
|
||||
authorBadge: undefined,
|
||||
canDeleteForEveryone: false,
|
||||
canDownload: true,
|
||||
canReply: true,
|
||||
|
@ -99,6 +101,7 @@ const items: Record<string, TimelineItemType> = {
|
|||
type: 'message',
|
||||
data: {
|
||||
author: getDefaultConversation({}),
|
||||
authorBadge: undefined,
|
||||
canDeleteForEveryone: false,
|
||||
canDownload: true,
|
||||
canReply: true,
|
||||
|
@ -188,6 +191,7 @@ const items: Record<string, TimelineItemType> = {
|
|||
type: 'message',
|
||||
data: {
|
||||
author: getDefaultConversation({}),
|
||||
authorBadge: undefined,
|
||||
canDeleteForEveryone: false,
|
||||
canDownload: true,
|
||||
canReply: true,
|
||||
|
@ -209,6 +213,7 @@ const items: Record<string, TimelineItemType> = {
|
|||
type: 'message',
|
||||
data: {
|
||||
author: getDefaultConversation({}),
|
||||
authorBadge: undefined,
|
||||
canDeleteForEveryone: false,
|
||||
canDownload: true,
|
||||
canReply: true,
|
||||
|
@ -230,6 +235,7 @@ const items: Record<string, TimelineItemType> = {
|
|||
type: 'message',
|
||||
data: {
|
||||
author: getDefaultConversation({}),
|
||||
authorBadge: undefined,
|
||||
canDeleteForEveryone: false,
|
||||
canDownload: true,
|
||||
canReply: true,
|
||||
|
@ -251,6 +257,7 @@ const items: Record<string, TimelineItemType> = {
|
|||
type: 'message',
|
||||
data: {
|
||||
author: getDefaultConversation({}),
|
||||
authorBadge: undefined,
|
||||
canDeleteForEveryone: false,
|
||||
canDownload: true,
|
||||
canReply: true,
|
||||
|
@ -273,6 +280,7 @@ const items: Record<string, TimelineItemType> = {
|
|||
type: 'message',
|
||||
data: {
|
||||
author: getDefaultConversation({}),
|
||||
authorBadge: undefined,
|
||||
canDeleteForEveryone: false,
|
||||
canDownload: true,
|
||||
canReply: true,
|
||||
|
@ -391,6 +399,7 @@ const renderItem = ({
|
|||
nextItem={undefined}
|
||||
i18n={i18n}
|
||||
interactionMode="keyboard"
|
||||
theme={ThemeType.light}
|
||||
containerElementRef={containerElementRef}
|
||||
containerWidthBreakpoint={containerWidthBreakpoint}
|
||||
conversationId=""
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// Copyright 2019-2021 Signal Messenger, LLC
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
|
||||
import { debounce, get, isNumber, pick, identity } from 'lodash';
|
||||
import { debounce, get, isNumber, pick } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import type { CSSProperties, ReactChild, ReactNode, RefObject } from 'react';
|
||||
import React from 'react';
|
||||
|
@ -226,7 +226,7 @@ type StateType = {
|
|||
const getActions = createSelector(
|
||||
// It is expensive to pick so many properties out of the `props` object so we
|
||||
// use `createSelector` to memoize them by the last seen `props` object.
|
||||
identity,
|
||||
(props: PropsType) => props,
|
||||
|
||||
(props: PropsType): PropsActionsType => {
|
||||
const unsafe = pick(props, [
|
||||
|
|
|
@ -16,6 +16,7 @@ import { CallMode } from '../../types/Calling';
|
|||
import { AvatarColors } from '../../types/Colors';
|
||||
import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation';
|
||||
import { WidthBreakpoint } from '../_util';
|
||||
import { ThemeType } from '../../types/Util';
|
||||
|
||||
const i18n = setupI18n('en', enMessages);
|
||||
|
||||
|
@ -53,6 +54,7 @@ const getDefaultProps = () => ({
|
|||
id: 'asdf',
|
||||
isSelected: false,
|
||||
interactionMode: 'keyboard' as const,
|
||||
theme: ThemeType.light,
|
||||
selectMessage: action('selectMessage'),
|
||||
reactToMessage: action('reactToMessage'),
|
||||
checkForAccount: action('checkForAccount'),
|
||||
|
|
|
@ -148,7 +148,7 @@ type PropsLocalType = {
|
|||
renderUniversalTimerNotification: () => JSX.Element;
|
||||
i18n: LocalizerType;
|
||||
interactionMode: InteractionModeType;
|
||||
theme?: ThemeType;
|
||||
theme: ThemeType;
|
||||
previousItem: undefined | TimelineItemType;
|
||||
nextItem: undefined | TimelineItemType;
|
||||
};
|
||||
|
|
|
@ -134,6 +134,8 @@ import type { LinkPreviewType } from '../types/message/LinkPreviews';
|
|||
import * as log from '../logging/log';
|
||||
import * as Bytes from '../Bytes';
|
||||
import { computeHash } from '../Crypto';
|
||||
import type { PreferredBadgeSelectorType } from '../state/selectors/badges';
|
||||
import { getPreferredBadgeSelector } from '../state/selectors/badges';
|
||||
|
||||
/* eslint-disable camelcase */
|
||||
/* eslint-disable more/no-then */
|
||||
|
@ -395,6 +397,13 @@ export class MessageModel extends window.Backbone.Model<MessageAttributesType> {
|
|||
ourConversationId,
|
||||
ourNumber: window.textsecure.storage.user.getNumber(),
|
||||
ourUuid: window.textsecure.storage.user.getCheckedUuid().toString(),
|
||||
preferredBadgeSelector: (
|
||||
...args: Parameters<PreferredBadgeSelectorType>
|
||||
) => {
|
||||
const state = window.reduxStore.getState();
|
||||
const preferredBadgeSelector = getPreferredBadgeSelector(state);
|
||||
return preferredBadgeSelector(...args);
|
||||
},
|
||||
regionCode: window.storage.get('regionCode', 'ZZ'),
|
||||
accountSelector: (identifier?: string) => {
|
||||
const state = window.reduxStore.getState();
|
||||
|
|
|
@ -50,11 +50,13 @@ export const getBadgesSelector = createSelector(
|
|||
}
|
||||
);
|
||||
|
||||
export type PreferredBadgeSelectorType = (
|
||||
conversationBadges: ReadonlyArray<Pick<BadgeType, 'id'>>
|
||||
) => undefined | BadgeType;
|
||||
|
||||
export const getPreferredBadgeSelector = createSelector(
|
||||
getBadgesById,
|
||||
badgesById => (
|
||||
conversationBadges: ReadonlyArray<Pick<BadgeType, 'id'>>
|
||||
): undefined | BadgeType => {
|
||||
(badgesById): PreferredBadgeSelectorType => conversationBadges => {
|
||||
const firstId: undefined | string = conversationBadges[0]?.id;
|
||||
if (!firstId) {
|
||||
return undefined;
|
||||
|
|
|
@ -49,6 +49,8 @@ import { getPinnedConversationIds } from './items';
|
|||
import { getPropsForBubble } from './message';
|
||||
import type { CallSelectorType, CallStateType } from './calling';
|
||||
import { getActiveCall, getCallSelector } from './calling';
|
||||
import type { PreferredBadgeSelectorType } from './badges';
|
||||
import { getPreferredBadgeSelector } from './badges';
|
||||
import type { AccountSelectorType } from './accounts';
|
||||
import { getAccountSelector } from './accounts';
|
||||
import * as log from '../../logging/log';
|
||||
|
@ -768,6 +770,7 @@ export const getMessageSelector = createSelector(
|
|||
getMessages,
|
||||
getSelectedMessage,
|
||||
getConversationSelector,
|
||||
getPreferredBadgeSelector,
|
||||
getRegionCode,
|
||||
getUserNumber,
|
||||
getUserUuid,
|
||||
|
@ -781,6 +784,7 @@ export const getMessageSelector = createSelector(
|
|||
messageLookup: MessageLookupType,
|
||||
selectedMessage: SelectedMessageType | undefined,
|
||||
conversationSelector: GetConversationByIdType,
|
||||
preferredBadgeSelector: PreferredBadgeSelectorType,
|
||||
regionCode: string,
|
||||
ourNumber: string,
|
||||
ourUuid: UUIDStringType,
|
||||
|
@ -801,6 +805,7 @@ export const getMessageSelector = createSelector(
|
|||
ourConversationId,
|
||||
ourNumber,
|
||||
ourUuid,
|
||||
preferredBadgeSelector,
|
||||
regionCode,
|
||||
selectedMessageId: selectedMessage?.id,
|
||||
selectedMessageCounter: selectedMessage?.counter,
|
||||
|
|
|
@ -63,6 +63,7 @@ import type {
|
|||
} from '../ducks/conversations';
|
||||
|
||||
import type { AccountSelectorType } from './accounts';
|
||||
import type { PreferredBadgeSelectorType } from './badges';
|
||||
import type { CallSelectorType, CallStateType } from './calling';
|
||||
import type {
|
||||
GetConversationByIdType,
|
||||
|
@ -81,6 +82,7 @@ import {
|
|||
someSendStatus,
|
||||
} from '../../messages/MessageSendState';
|
||||
import * as log from '../../logging/log';
|
||||
import type { BadgeType } from '../../badges/types';
|
||||
|
||||
const THREE_HOURS = 3 * 60 * 60 * 1000;
|
||||
|
||||
|
@ -106,6 +108,7 @@ export type GetPropsForBubbleOptions = Readonly<{
|
|||
ourConversationId: string;
|
||||
ourNumber?: string;
|
||||
ourUuid: UUIDStringType;
|
||||
preferredBadgeSelector: PreferredBadgeSelectorType;
|
||||
selectedMessageId?: string;
|
||||
selectedMessageCounter?: number;
|
||||
regionCode: string;
|
||||
|
@ -241,11 +244,7 @@ export const getAttachmentsForMessage = createSelectorCreator(memoizeByRoot)(
|
|||
|
||||
({ sticker }: MessageWithUIFieldsType) => sticker,
|
||||
({ attachments }: MessageWithUIFieldsType) => attachments,
|
||||
(
|
||||
_: MessageWithUIFieldsType,
|
||||
sticker: MessageWithUIFieldsType['sticker'],
|
||||
attachments: MessageWithUIFieldsType['attachments'] = []
|
||||
): Array<AttachmentType> => {
|
||||
(_, sticker, attachments = []): Array<AttachmentType> => {
|
||||
if (sticker && sticker.data) {
|
||||
const { data } = sticker;
|
||||
|
||||
|
@ -298,7 +297,7 @@ export const processBodyRanges = createSelectorCreator(memoizeByRoot, isEqual)(
|
|||
})
|
||||
.sort((a, b) => b.start - a.start);
|
||||
},
|
||||
(_: MessageWithUIFieldsType, ranges?: BodyRangesType) => ranges
|
||||
(_, ranges): undefined | BodyRangesType => ranges
|
||||
);
|
||||
|
||||
const getAuthorForMessage = createSelectorCreator(memoizeByRoot)(
|
||||
|
@ -307,13 +306,11 @@ const getAuthorForMessage = createSelectorCreator(memoizeByRoot)(
|
|||
|
||||
getContact,
|
||||
|
||||
(
|
||||
_: MessageWithUIFieldsType,
|
||||
convo: ConversationType
|
||||
): PropsData['author'] => {
|
||||
(_, convo: ConversationType): PropsData['author'] => {
|
||||
const {
|
||||
acceptedMessageRequest,
|
||||
avatarPath,
|
||||
badges,
|
||||
color,
|
||||
id,
|
||||
isMe,
|
||||
|
@ -328,6 +325,7 @@ const getAuthorForMessage = createSelectorCreator(memoizeByRoot)(
|
|||
const unsafe = {
|
||||
acceptedMessageRequest,
|
||||
avatarPath,
|
||||
badges,
|
||||
color,
|
||||
id,
|
||||
isMe,
|
||||
|
@ -348,25 +346,28 @@ const getAuthorForMessage = createSelectorCreator(memoizeByRoot)(
|
|||
const getCachedAuthorForMessage = createSelectorCreator(memoizeByRoot, isEqual)(
|
||||
// `memoizeByRoot` requirement
|
||||
identity,
|
||||
|
||||
getAuthorForMessage,
|
||||
(_, author): PropsData['author'] => author
|
||||
);
|
||||
|
||||
(
|
||||
_: MessageWithUIFieldsType,
|
||||
author: PropsData['author']
|
||||
): PropsData['author'] => author
|
||||
const getAuthorBadgeForMessage: (
|
||||
message: MessageWithUIFieldsType,
|
||||
options: {
|
||||
preferredBadgeSelector: PreferredBadgeSelectorType;
|
||||
}
|
||||
) => undefined | BadgeType = createSelectorCreator(memoizeByRoot, isEqual)(
|
||||
// `memoizeByRoot` requirement
|
||||
identity,
|
||||
(_, { preferredBadgeSelector }) => preferredBadgeSelector,
|
||||
getAuthorForMessage,
|
||||
(_, preferredBadgeSelector, author) => preferredBadgeSelector(author.badges)
|
||||
);
|
||||
|
||||
export const getPreviewsForMessage = createSelectorCreator(memoizeByRoot)(
|
||||
// `memoizeByRoot` requirement
|
||||
identity,
|
||||
|
||||
({ preview }: MessageWithUIFieldsType) => preview,
|
||||
|
||||
(
|
||||
_: MessageWithUIFieldsType,
|
||||
previews: MessageWithUIFieldsType['preview'] = []
|
||||
): Array<LinkPreviewType> => {
|
||||
(_, previews = []): Array<LinkPreviewType> => {
|
||||
return previews.map(preview => ({
|
||||
...preview,
|
||||
isStickerPack: isStickerPack(preview.url),
|
||||
|
@ -435,7 +436,7 @@ export const getReactionsForMessage = createSelectorCreator(
|
|||
return [...formattedReactions];
|
||||
},
|
||||
|
||||
(_: MessageWithUIFieldsType, reactions: PropsData['reactions']) => reactions
|
||||
(_, reactions): PropsData['reactions'] => reactions
|
||||
);
|
||||
|
||||
export const getPropsForQuote = createSelectorCreator(memoizeByRoot, isEqual)(
|
||||
|
@ -503,7 +504,7 @@ export const getPropsForQuote = createSelectorCreator(memoizeByRoot, isEqual)(
|
|||
};
|
||||
},
|
||||
|
||||
(_: unknown, quote: PropsData['quote']) => quote
|
||||
(_, quote): PropsData['quote'] => quote
|
||||
);
|
||||
|
||||
export type GetPropsForMessageOptions = Pick<
|
||||
|
@ -512,6 +513,7 @@ export type GetPropsForMessageOptions = Pick<
|
|||
| 'ourConversationId'
|
||||
| 'ourUuid'
|
||||
| 'ourNumber'
|
||||
| 'preferredBadgeSelector'
|
||||
| 'selectedMessageId'
|
||||
| 'selectedMessageCounter'
|
||||
| 'regionCode'
|
||||
|
@ -640,22 +642,29 @@ const getShallowPropsForMessage = createSelectorCreator(memoizeByRoot, isEqual)(
|
|||
(_: unknown, props: ShallowPropsType) => props
|
||||
);
|
||||
|
||||
export const getPropsForMessage = createSelectorCreator(memoizeByRoot)(
|
||||
export const getPropsForMessage: (
|
||||
message: MessageWithUIFieldsType,
|
||||
options: GetPropsForMessageOptions
|
||||
) => Omit<PropsForMessage, 'renderingContext'> = createSelectorCreator(
|
||||
memoizeByRoot
|
||||
)(
|
||||
// `memoizeByRoot` requirement
|
||||
identity,
|
||||
|
||||
getAttachmentsForMessage,
|
||||
processBodyRanges,
|
||||
getCachedAuthorForMessage,
|
||||
getAuthorBadgeForMessage,
|
||||
getPreviewsForMessage,
|
||||
getReactionsForMessage,
|
||||
getPropsForQuote,
|
||||
getShallowPropsForMessage,
|
||||
(
|
||||
_: unknown,
|
||||
_,
|
||||
attachments: Array<AttachmentType>,
|
||||
bodyRanges: BodyRangesType | undefined,
|
||||
author: PropsData['author'],
|
||||
authorBadge: undefined | BadgeType,
|
||||
previews: Array<LinkPreviewType>,
|
||||
reactions: PropsData['reactions'],
|
||||
quote: PropsData['quote'],
|
||||
|
@ -664,6 +673,7 @@ export const getPropsForMessage = createSelectorCreator(memoizeByRoot)(
|
|||
return {
|
||||
attachments,
|
||||
author,
|
||||
authorBadge,
|
||||
bodyRanges,
|
||||
previews,
|
||||
quote,
|
||||
|
@ -678,7 +688,8 @@ export const getBubblePropsForMessage = createSelectorCreator(memoizeByRoot)(
|
|||
identity,
|
||||
|
||||
getPropsForMessage,
|
||||
(_: unknown, data: ReturnType<typeof getPropsForMessage>) => ({
|
||||
|
||||
(_, data): TimelineItemType => ({
|
||||
type: 'message' as const,
|
||||
data,
|
||||
})
|
||||
|
|
|
@ -8,7 +8,8 @@ import { MessageDetail } from '../../components/conversation/MessageDetail';
|
|||
|
||||
import { mapDispatchToProps } from '../actions';
|
||||
import type { StateType } from '../reducer';
|
||||
import { getIntl, getInteractionMode } from '../selectors/user';
|
||||
import { getPreferredBadgeSelector } from '../selectors/badges';
|
||||
import { getIntl, getInteractionMode, getTheme } from '../selectors/user';
|
||||
import { renderAudioAttachment } from './renderAudioAttachment';
|
||||
import { renderEmojiPicker } from './renderEmojiPicker';
|
||||
import { renderReactionPicker } from './renderReactionPicker';
|
||||
|
@ -17,11 +18,13 @@ import { getContactNameColorSelector } from '../selectors/conversations';
|
|||
export { Contact } from '../../components/conversation/MessageDetail';
|
||||
export type OwnProps = Omit<
|
||||
MessageDetailProps,
|
||||
| 'getPreferredBadge'
|
||||
| 'i18n'
|
||||
| 'interactionMode'
|
||||
| 'renderAudioAttachment'
|
||||
| 'renderEmojiPicker'
|
||||
| 'renderReactionPicker'
|
||||
| 'theme'
|
||||
>;
|
||||
|
||||
const mapStateToProps = (
|
||||
|
@ -62,6 +65,8 @@ const mapStateToProps = (
|
|||
)
|
||||
: undefined;
|
||||
|
||||
const getPreferredBadge = getPreferredBadgeSelector(state);
|
||||
|
||||
return {
|
||||
contacts,
|
||||
contactNameColor,
|
||||
|
@ -70,8 +75,10 @@ const mapStateToProps = (
|
|||
receivedAt,
|
||||
sentAt,
|
||||
|
||||
getPreferredBadge,
|
||||
i18n: getIntl(state),
|
||||
interactionMode: getInteractionMode(state),
|
||||
theme: getTheme(state),
|
||||
|
||||
showSafetyNumber,
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ import { strictAssert } from './assert';
|
|||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
export function memoizeByRoot<F extends Function>(
|
||||
fn: F,
|
||||
equalityCheck?: <T>(a: T, b: T, index: number) => boolean
|
||||
equalityCheck?: <T>(a: T, b: T) => boolean
|
||||
): F {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-types
|
||||
const cache = new WeakMap<object, Function>();
|
||||
|
|
|
@ -15756,10 +15756,10 @@ requizzle@^0.2.2:
|
|||
dependencies:
|
||||
lodash "^4.17.14"
|
||||
|
||||
reselect@4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.0.0.tgz#f2529830e5d3d0e021408b246a206ef4ea4437f7"
|
||||
integrity sha512-qUgANli03jjAyGlnbYVAV5vvnOmJnODyABz51RdBN7M4WaVu8mecZWgyQNkG8Yqe3KRGRt0l4K4B3XVEULC4CA==
|
||||
reselect@4.1.2:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.2.tgz#7bf642992d143d4f3b0f2dca8aa52018808a1d51"
|
||||
integrity sha512-wg60ebcPOtxcptIUfrr7Jt3h4BR86cCW3R7y4qt65lnNb4yz4QgrXcbSioVsIOYguyz42+XTHIyJ5TEruzkFgQ==
|
||||
|
||||
reserved-words@^0.1.2:
|
||||
version "0.1.2"
|
||||
|
|
Loading…
Reference in New Issue