Add badges to all conversation lists
This commit is contained in:
parent
2c4dfc74c4
commit
2cbcd59609
22 changed files with 131 additions and 83 deletions
|
@ -17,6 +17,7 @@ import { ContactCheckboxDisabledReason } from './conversationList/ContactCheckbo
|
||||||
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
|
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
|
||||||
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 { ThemeType } from '../types/Util';
|
||||||
import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext';
|
import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext';
|
||||||
import { UUID } from '../types/UUID';
|
import { UUID } from '../types/UUID';
|
||||||
|
|
||||||
|
@ -60,6 +61,7 @@ const Wrapper = ({
|
||||||
height: 350,
|
height: 350,
|
||||||
}}
|
}}
|
||||||
rowCount={rows.length}
|
rowCount={rows.length}
|
||||||
|
getPreferredBadge={() => undefined}
|
||||||
getRow={(index: number) => rows[index]}
|
getRow={(index: number) => rows[index]}
|
||||||
shouldRecomputeRowHeights={false}
|
shouldRecomputeRowHeights={false}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
@ -72,11 +74,13 @@ const Wrapper = ({
|
||||||
bodyRanges={[]}
|
bodyRanges={[]}
|
||||||
conversationId="marc-convo"
|
conversationId="marc-convo"
|
||||||
from={defaultConversations[0]}
|
from={defaultConversations[0]}
|
||||||
|
getPreferredBadge={() => undefined}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
id={id}
|
id={id}
|
||||||
openConversationInternal={action('openConversationInternal')}
|
openConversationInternal={action('openConversationInternal')}
|
||||||
sentAt={1587358800000}
|
sentAt={1587358800000}
|
||||||
snippet="Lorem <<left>>ipsum<<right>> wow"
|
snippet="Lorem <<left>>ipsum<<right>> wow"
|
||||||
|
theme={ThemeType.light}
|
||||||
to={defaultConversations[1]}
|
to={defaultConversations[1]}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -8,17 +8,16 @@ import { List } from 'react-virtualized';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { get, pick } from 'lodash';
|
import { get, pick } from 'lodash';
|
||||||
|
|
||||||
import { getOwn } from '../util/getOwn';
|
|
||||||
import { missingCaseError } from '../util/missingCaseError';
|
import { missingCaseError } from '../util/missingCaseError';
|
||||||
import { assert } from '../util/assert';
|
import { assert } from '../util/assert';
|
||||||
import type { LocalizerType, ThemeType } from '../types/Util';
|
import type { LocalizerType, ThemeType } from '../types/Util';
|
||||||
import { ScrollBehavior } from '../types/Util';
|
import { ScrollBehavior } from '../types/Util';
|
||||||
import { getConversationListWidthBreakpoint } from './_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 type { PropsData as ConversationListItemPropsType } from './conversationList/ConversationListItem';
|
||||||
import { ConversationListItem } 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 { ContactListItem } from './conversationList/ContactListItem';
|
||||||
import type { ContactCheckboxDisabledReason } from './conversationList/ContactCheckbox';
|
import type { ContactCheckboxDisabledReason } from './conversationList/ContactCheckbox';
|
||||||
import { ContactCheckbox as ContactCheckboxComponent } from './conversationList/ContactCheckbox';
|
import { ContactCheckbox as ContactCheckboxComponent } from './conversationList/ContactCheckbox';
|
||||||
|
@ -116,7 +115,6 @@ export type Row =
|
||||||
| UsernameRowType;
|
| UsernameRowType;
|
||||||
|
|
||||||
export type PropsType = {
|
export type PropsType = {
|
||||||
badgesById?: Record<string, BadgeType>;
|
|
||||||
dimensions?: {
|
dimensions?: {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
|
@ -131,6 +129,7 @@ export type PropsType = {
|
||||||
shouldRecomputeRowHeights: boolean;
|
shouldRecomputeRowHeights: boolean;
|
||||||
scrollable?: boolean;
|
scrollable?: boolean;
|
||||||
|
|
||||||
|
getPreferredBadge: PreferredBadgeSelectorType;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
theme: ThemeType;
|
theme: ThemeType;
|
||||||
|
|
||||||
|
@ -150,8 +149,8 @@ const NORMAL_ROW_HEIGHT = 76;
|
||||||
const HEADER_ROW_HEIGHT = 40;
|
const HEADER_ROW_HEIGHT = 40;
|
||||||
|
|
||||||
export const ConversationList: React.FC<PropsType> = ({
|
export const ConversationList: React.FC<PropsType> = ({
|
||||||
badgesById,
|
|
||||||
dimensions,
|
dimensions,
|
||||||
|
getPreferredBadge,
|
||||||
getRow,
|
getRow,
|
||||||
i18n,
|
i18n,
|
||||||
onClickArchiveButton,
|
onClickArchiveButton,
|
||||||
|
@ -231,8 +230,10 @@ export const ConversationList: React.FC<PropsType> = ({
|
||||||
result = (
|
result = (
|
||||||
<ContactListItem
|
<ContactListItem
|
||||||
{...row.contact}
|
{...row.contact}
|
||||||
|
badge={getPreferredBadge(row.contact.badges)}
|
||||||
onClick={isClickable ? onSelectConversation : undefined}
|
onClick={isClickable ? onSelectConversation : undefined}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -241,10 +242,12 @@ export const ConversationList: React.FC<PropsType> = ({
|
||||||
result = (
|
result = (
|
||||||
<ContactCheckboxComponent
|
<ContactCheckboxComponent
|
||||||
{...row.contact}
|
{...row.contact}
|
||||||
|
badge={getPreferredBadge(row.contact.badges)}
|
||||||
isChecked={row.isChecked}
|
isChecked={row.isChecked}
|
||||||
disabledReason={row.disabledReason}
|
disabledReason={row.disabledReason}
|
||||||
onClick={onClickContactCheckbox}
|
onClick={onClickContactCheckbox}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
|
theme={theme}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
|
@ -274,12 +277,6 @@ export const ConversationList: React.FC<PropsType> = ({
|
||||||
'unreadCount',
|
'unreadCount',
|
||||||
]);
|
]);
|
||||||
const { badges, title, unreadCount, lastMessage } = itemProps;
|
const { badges, title, unreadCount, lastMessage } = itemProps;
|
||||||
|
|
||||||
let badge: undefined | BadgeType;
|
|
||||||
if (badgesById && badges[0]) {
|
|
||||||
badge = getOwn(badgesById, badges[0].id);
|
|
||||||
}
|
|
||||||
|
|
||||||
result = (
|
result = (
|
||||||
<div
|
<div
|
||||||
aria-label={i18n('ConversationList__aria-label', {
|
aria-label={i18n('ConversationList__aria-label', {
|
||||||
|
@ -293,7 +290,7 @@ export const ConversationList: React.FC<PropsType> = ({
|
||||||
<ConversationListItem
|
<ConversationListItem
|
||||||
{...itemProps}
|
{...itemProps}
|
||||||
key={key}
|
key={key}
|
||||||
badge={badge}
|
badge={getPreferredBadge(badges)}
|
||||||
onClick={onSelectConversation}
|
onClick={onSelectConversation}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
@ -361,7 +358,7 @@ export const ConversationList: React.FC<PropsType> = ({
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
badgesById,
|
getPreferredBadge,
|
||||||
getRow,
|
getRow,
|
||||||
i18n,
|
i18n,
|
||||||
onClickArchiveButton,
|
onClickArchiveButton,
|
||||||
|
|
|
@ -398,6 +398,7 @@ export const ForwardMessageModal: FunctionComponent<PropsType> = ({
|
||||||
>
|
>
|
||||||
<ConversationList
|
<ConversationList
|
||||||
dimensions={contentRect.bounds}
|
dimensions={contentRect.bounds}
|
||||||
|
getPreferredBadge={getPreferredBadge}
|
||||||
getRow={getRow}
|
getRow={getRow}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
onClickArchiveButton={shouldNeverBeCalled}
|
onClickArchiveButton={shouldNeverBeCalled}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import type { ConversationType } from '../state/ducks/conversations';
|
||||||
import { MessageSearchResult } from './conversationList/MessageSearchResult';
|
import { MessageSearchResult } from './conversationList/MessageSearchResult';
|
||||||
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 { ThemeType } from '../types/Util';
|
||||||
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
|
import { getDefaultConversation } from '../test-both/helpers/getDefaultConversation';
|
||||||
import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext';
|
import { StorybookThemeContext } from '../../.storybook/StorybookThemeContext';
|
||||||
|
|
||||||
|
@ -81,7 +82,6 @@ const defaultModeSpecificProps = {
|
||||||
const emptySearchResultsGroup = { isLoading: false, results: [] };
|
const emptySearchResultsGroup = { isLoading: false, results: [] };
|
||||||
|
|
||||||
const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||||
badgesById: {},
|
|
||||||
cantAddContactToGroup: action('cantAddContactToGroup'),
|
cantAddContactToGroup: action('cantAddContactToGroup'),
|
||||||
canResizeLeftPane: true,
|
canResizeLeftPane: true,
|
||||||
clearGroupCreationError: action('clearGroupCreationError'),
|
clearGroupCreationError: action('clearGroupCreationError'),
|
||||||
|
@ -93,6 +93,7 @@ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||||
composeReplaceAvatar: action('composeReplaceAvatar'),
|
composeReplaceAvatar: action('composeReplaceAvatar'),
|
||||||
composeSaveAvatarToDisk: action('composeSaveAvatarToDisk'),
|
composeSaveAvatarToDisk: action('composeSaveAvatarToDisk'),
|
||||||
createGroup: action('createGroup'),
|
createGroup: action('createGroup'),
|
||||||
|
getPreferredBadge: () => undefined,
|
||||||
i18n,
|
i18n,
|
||||||
modeSpecificProps: defaultModeSpecificProps,
|
modeSpecificProps: defaultModeSpecificProps,
|
||||||
preferredWidthFromStorage: 320,
|
preferredWidthFromStorage: 320,
|
||||||
|
@ -112,11 +113,13 @@ const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||||
bodyRanges={[]}
|
bodyRanges={[]}
|
||||||
conversationId="marc-convo"
|
conversationId="marc-convo"
|
||||||
from={defaultConversations[0]}
|
from={defaultConversations[0]}
|
||||||
|
getPreferredBadge={() => undefined}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
id={id}
|
id={id}
|
||||||
openConversationInternal={action('openConversationInternal')}
|
openConversationInternal={action('openConversationInternal')}
|
||||||
sentAt={1587358800000}
|
sentAt={1587358800000}
|
||||||
snippet="Lorem <<left>>ipsum<<right>> wow"
|
snippet="Lorem <<left>>ipsum<<right>> wow"
|
||||||
|
theme={ThemeType.light}
|
||||||
to={defaultConversations[1]}
|
to={defaultConversations[1]}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
|
|
|
@ -25,7 +25,7 @@ import { LeftPaneSetGroupMetadataHelper } from './leftPane/LeftPaneSetGroupMetad
|
||||||
import * as OS from '../OS';
|
import * as OS from '../OS';
|
||||||
import type { LocalizerType, ThemeType } from '../types/Util';
|
import type { LocalizerType, ThemeType } from '../types/Util';
|
||||||
import { ScrollBehavior } 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 { usePrevious } from '../hooks/usePrevious';
|
||||||
import { missingCaseError } from '../util/missingCaseError';
|
import { missingCaseError } from '../util/missingCaseError';
|
||||||
import { strictAssert } from '../util/assert';
|
import { strictAssert } from '../util/assert';
|
||||||
|
@ -83,8 +83,8 @@ export type PropsType = {
|
||||||
| ({
|
| ({
|
||||||
mode: LeftPaneMode.SetGroupMetadata;
|
mode: LeftPaneMode.SetGroupMetadata;
|
||||||
} & LeftPaneSetGroupMetadataPropsType);
|
} & LeftPaneSetGroupMetadataPropsType);
|
||||||
|
getPreferredBadge: PreferredBadgeSelectorType;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
badgesById: Record<string, BadgeType>;
|
|
||||||
preferredWidthFromStorage: number;
|
preferredWidthFromStorage: number;
|
||||||
selectedConversationId: undefined | string;
|
selectedConversationId: undefined | string;
|
||||||
selectedMessageId: undefined | string;
|
selectedMessageId: undefined | string;
|
||||||
|
@ -147,7 +147,6 @@ export type PropsType = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LeftPane: React.FC<PropsType> = ({
|
export const LeftPane: React.FC<PropsType> = ({
|
||||||
badgesById,
|
|
||||||
cantAddContactToGroup,
|
cantAddContactToGroup,
|
||||||
canResizeLeftPane,
|
canResizeLeftPane,
|
||||||
challengeStatus,
|
challengeStatus,
|
||||||
|
@ -160,6 +159,7 @@ export const LeftPane: React.FC<PropsType> = ({
|
||||||
composeReplaceAvatar,
|
composeReplaceAvatar,
|
||||||
composeSaveAvatarToDisk,
|
composeSaveAvatarToDisk,
|
||||||
createGroup,
|
createGroup,
|
||||||
|
getPreferredBadge,
|
||||||
i18n,
|
i18n,
|
||||||
modeSpecificProps,
|
modeSpecificProps,
|
||||||
openConversationInternal,
|
openConversationInternal,
|
||||||
|
@ -573,11 +573,11 @@ export const LeftPane: React.FC<PropsType> = ({
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
>
|
>
|
||||||
<ConversationList
|
<ConversationList
|
||||||
badgesById={badgesById}
|
|
||||||
dimensions={{
|
dimensions={{
|
||||||
width,
|
width,
|
||||||
height: contentRect.bounds?.height || 0,
|
height: contentRect.bounds?.height || 0,
|
||||||
}}
|
}}
|
||||||
|
getPreferredBadge={getPreferredBadge}
|
||||||
getRow={getRow}
|
getRow={getRow}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
onClickArchiveButton={showArchivedConversations}
|
onClickArchiveButton={showArchivedConversations}
|
||||||
|
|
|
@ -31,6 +31,7 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||||
candidateContacts: allCandidateContacts,
|
candidateContacts: allCandidateContacts,
|
||||||
clearRequestError: action('clearRequestError'),
|
clearRequestError: action('clearRequestError'),
|
||||||
conversationIdsAlreadyInGroup: new Set(),
|
conversationIdsAlreadyInGroup: new Set(),
|
||||||
|
getPreferredBadge: () => undefined,
|
||||||
groupTitle: 'Tahoe Trip',
|
groupTitle: 'Tahoe Trip',
|
||||||
i18n,
|
i18n,
|
||||||
onClose: action('onClose'),
|
onClose: action('onClose'),
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
AddGroupMemberErrorDialogMode,
|
AddGroupMemberErrorDialogMode,
|
||||||
} from '../../AddGroupMemberErrorDialog';
|
} from '../../AddGroupMemberErrorDialog';
|
||||||
import type { ConversationType } from '../../../state/ducks/conversations';
|
import type { ConversationType } from '../../../state/ducks/conversations';
|
||||||
|
import type { PreferredBadgeSelectorType } from '../../../state/selectors/badges';
|
||||||
import {
|
import {
|
||||||
getGroupSizeRecommendedLimit,
|
getGroupSizeRecommendedLimit,
|
||||||
getGroupSizeHardLimit,
|
getGroupSizeHardLimit,
|
||||||
|
@ -30,6 +31,7 @@ type PropsType = {
|
||||||
candidateContacts: ReadonlyArray<ConversationType>;
|
candidateContacts: ReadonlyArray<ConversationType>;
|
||||||
clearRequestError: () => void;
|
clearRequestError: () => void;
|
||||||
conversationIdsAlreadyInGroup: Set<string>;
|
conversationIdsAlreadyInGroup: Set<string>;
|
||||||
|
getPreferredBadge: PreferredBadgeSelectorType;
|
||||||
groupTitle: string;
|
groupTitle: string;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
makeRequest: (conversationIds: ReadonlyArray<string>) => Promise<void>;
|
makeRequest: (conversationIds: ReadonlyArray<string>) => Promise<void>;
|
||||||
|
@ -147,6 +149,7 @@ export const AddGroupMembersModal: FunctionComponent<PropsType> = ({
|
||||||
candidateContacts,
|
candidateContacts,
|
||||||
clearRequestError,
|
clearRequestError,
|
||||||
conversationIdsAlreadyInGroup,
|
conversationIdsAlreadyInGroup,
|
||||||
|
getPreferredBadge,
|
||||||
groupTitle,
|
groupTitle,
|
||||||
i18n,
|
i18n,
|
||||||
onClose,
|
onClose,
|
||||||
|
@ -279,6 +282,7 @@ export const AddGroupMembersModal: FunctionComponent<PropsType> = ({
|
||||||
confirmAdds={confirmAdds}
|
confirmAdds={confirmAdds}
|
||||||
contactLookup={contactLookup}
|
contactLookup={contactLookup}
|
||||||
conversationIdsAlreadyInGroup={conversationIdsAlreadyInGroup}
|
conversationIdsAlreadyInGroup={conversationIdsAlreadyInGroup}
|
||||||
|
getPreferredBadge={getPreferredBadge}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
maxGroupSize={maxGroupSize}
|
maxGroupSize={maxGroupSize}
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import { useRestoreFocus } from '../../../../hooks/useRestoreFocus';
|
||||||
import { missingCaseError } from '../../../../util/missingCaseError';
|
import { missingCaseError } from '../../../../util/missingCaseError';
|
||||||
import { filterAndSortConversationsByTitle } from '../../../../util/filterAndSortConversations';
|
import { filterAndSortConversationsByTitle } from '../../../../util/filterAndSortConversations';
|
||||||
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 { ContactPills } from '../../../ContactPills';
|
import { ContactPills } from '../../../ContactPills';
|
||||||
import { ContactPill } from '../../../ContactPill';
|
import { ContactPill } from '../../../ContactPill';
|
||||||
|
@ -28,6 +29,7 @@ type PropsType = {
|
||||||
confirmAdds: () => void;
|
confirmAdds: () => void;
|
||||||
contactLookup: Record<string, ConversationType>;
|
contactLookup: Record<string, ConversationType>;
|
||||||
conversationIdsAlreadyInGroup: Set<string>;
|
conversationIdsAlreadyInGroup: Set<string>;
|
||||||
|
getPreferredBadge: PreferredBadgeSelectorType;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
maxGroupSize: number;
|
maxGroupSize: number;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
|
@ -48,6 +50,7 @@ export const ChooseGroupMembersModal: FunctionComponent<PropsType> = ({
|
||||||
confirmAdds,
|
confirmAdds,
|
||||||
contactLookup,
|
contactLookup,
|
||||||
conversationIdsAlreadyInGroup,
|
conversationIdsAlreadyInGroup,
|
||||||
|
getPreferredBadge,
|
||||||
i18n,
|
i18n,
|
||||||
maxGroupSize,
|
maxGroupSize,
|
||||||
onClose,
|
onClose,
|
||||||
|
@ -192,6 +195,7 @@ export const ChooseGroupMembersModal: FunctionComponent<PropsType> = ({
|
||||||
>
|
>
|
||||||
<ConversationList
|
<ConversationList
|
||||||
dimensions={contentRect.bounds}
|
dimensions={contentRect.bounds}
|
||||||
|
getPreferredBadge={getPreferredBadge}
|
||||||
getRow={getRow}
|
getRow={getRow}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
onClickArchiveButton={shouldNeverBeCalled}
|
onClickArchiveButton={shouldNeverBeCalled}
|
||||||
|
|
|
@ -46,6 +46,7 @@ const createProps = (hasGroupLink = false, expireTimer?: number): Props => ({
|
||||||
}
|
}
|
||||||
: conversation,
|
: conversation,
|
||||||
hasGroupLink,
|
hasGroupLink,
|
||||||
|
getPreferredBadge: () => undefined,
|
||||||
i18n,
|
i18n,
|
||||||
isAdmin: false,
|
isAdmin: false,
|
||||||
isGroup: true,
|
isGroup: true,
|
||||||
|
@ -56,7 +57,6 @@ const createProps = (hasGroupLink = false, expireTimer?: number): Props => ({
|
||||||
isMe: i === 2,
|
isMe: i === 2,
|
||||||
}),
|
}),
|
||||||
})),
|
})),
|
||||||
preferredBadgeByConversation: {},
|
|
||||||
pendingApprovalMemberships: times(8, () => ({
|
pendingApprovalMemberships: times(8, () => ({
|
||||||
member: getDefaultConversation(),
|
member: getDefaultConversation(),
|
||||||
})),
|
})),
|
||||||
|
|
|
@ -6,6 +6,7 @@ import React, { useState } from 'react';
|
||||||
|
|
||||||
import { Button, ButtonIconType, ButtonVariant } from '../../Button';
|
import { Button, ButtonIconType, ButtonVariant } from '../../Button';
|
||||||
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 { getMutedUntilText } from '../../../util/getMutedUntilText';
|
import { getMutedUntilText } from '../../../util/getMutedUntilText';
|
||||||
|
|
||||||
|
@ -59,12 +60,12 @@ export type StateProps = {
|
||||||
candidateContactsToAdd: Array<ConversationType>;
|
candidateContactsToAdd: Array<ConversationType>;
|
||||||
conversation?: ConversationType;
|
conversation?: ConversationType;
|
||||||
hasGroupLink: boolean;
|
hasGroupLink: boolean;
|
||||||
|
getPreferredBadge: PreferredBadgeSelectorType;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
isAdmin: boolean;
|
isAdmin: boolean;
|
||||||
isGroup: boolean;
|
isGroup: boolean;
|
||||||
loadRecentMediaItems: (limit: number) => void;
|
loadRecentMediaItems: (limit: number) => void;
|
||||||
memberships: Array<GroupV2Membership>;
|
memberships: Array<GroupV2Membership>;
|
||||||
preferredBadgeByConversation: Record<string, BadgeType>;
|
|
||||||
pendingApprovalMemberships: ReadonlyArray<GroupV2RequestingMembership>;
|
pendingApprovalMemberships: ReadonlyArray<GroupV2RequestingMembership>;
|
||||||
pendingMemberships: ReadonlyArray<GroupV2PendingMembership>;
|
pendingMemberships: ReadonlyArray<GroupV2PendingMembership>;
|
||||||
setDisappearingMessages: (seconds: number) => void;
|
setDisappearingMessages: (seconds: number) => void;
|
||||||
|
@ -114,6 +115,7 @@ export const ConversationDetails: React.ComponentType<Props> = ({
|
||||||
conversation,
|
conversation,
|
||||||
deleteAvatarFromDisk,
|
deleteAvatarFromDisk,
|
||||||
hasGroupLink,
|
hasGroupLink,
|
||||||
|
getPreferredBadge,
|
||||||
i18n,
|
i18n,
|
||||||
isAdmin,
|
isAdmin,
|
||||||
isGroup,
|
isGroup,
|
||||||
|
@ -126,7 +128,6 @@ export const ConversationDetails: React.ComponentType<Props> = ({
|
||||||
onUnblock,
|
onUnblock,
|
||||||
pendingApprovalMemberships,
|
pendingApprovalMemberships,
|
||||||
pendingMemberships,
|
pendingMemberships,
|
||||||
preferredBadgeByConversation,
|
|
||||||
replaceAvatar,
|
replaceAvatar,
|
||||||
saveAvatarToDisk,
|
saveAvatarToDisk,
|
||||||
searchInConversation,
|
searchInConversation,
|
||||||
|
@ -235,6 +236,7 @@ export const ConversationDetails: React.ComponentType<Props> = ({
|
||||||
conversationIdsAlreadyInGroup={
|
conversationIdsAlreadyInGroup={
|
||||||
new Set(memberships.map(membership => membership.member.id))
|
new Set(memberships.map(membership => membership.member.id))
|
||||||
}
|
}
|
||||||
|
getPreferredBadge={getPreferredBadge}
|
||||||
groupTitle={conversation.title}
|
groupTitle={conversation.title}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
makeRequest={async conversationIds => {
|
makeRequest={async conversationIds => {
|
||||||
|
@ -459,9 +461,9 @@ export const ConversationDetails: React.ComponentType<Props> = ({
|
||||||
<ConversationDetailsMembershipList
|
<ConversationDetailsMembershipList
|
||||||
canAddNewMembers={canEditGroupInfo}
|
canAddNewMembers={canEditGroupInfo}
|
||||||
conversationId={conversation.id}
|
conversationId={conversation.id}
|
||||||
|
getPreferredBadge={getPreferredBadge}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
memberships={memberships}
|
memberships={memberships}
|
||||||
preferredBadgeByConversation={preferredBadgeByConversation}
|
|
||||||
showContactModal={showContactModal}
|
showContactModal={showContactModal}
|
||||||
startAddingNewMembers={() => {
|
startAddingNewMembers={() => {
|
||||||
setModalState(ModalState.AddingGroupMembers);
|
setModalState(ModalState.AddingGroupMembers);
|
||||||
|
|
|
@ -11,9 +11,7 @@ import { number } from '@storybook/addon-knobs';
|
||||||
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 { getFakeBadge } from '../../../test-both/helpers/getFakeBadge';
|
|
||||||
import { ThemeType } from '../../../types/Util';
|
import { ThemeType } from '../../../types/Util';
|
||||||
import type { BadgeType } from '../../../badges/types';
|
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
Props,
|
Props,
|
||||||
|
@ -48,20 +46,9 @@ const createProps = (overrideProps: Partial<Props>): Props => ({
|
||||||
? overrideProps.canAddNewMembers
|
? overrideProps.canAddNewMembers
|
||||||
: false,
|
: false,
|
||||||
conversationId: '123',
|
conversationId: '123',
|
||||||
|
getPreferredBadge: () => undefined,
|
||||||
i18n,
|
i18n,
|
||||||
memberships: overrideProps.memberships || [],
|
memberships: overrideProps.memberships || [],
|
||||||
preferredBadgeByConversation:
|
|
||||||
overrideProps.preferredBadgeByConversation ||
|
|
||||||
(overrideProps.memberships || []).reduce(
|
|
||||||
(result: Record<string, BadgeType>, { member }, index) =>
|
|
||||||
(index + 1) % 3 === 0
|
|
||||||
? {
|
|
||||||
...result,
|
|
||||||
[member.id]: getFakeBadge({ alternate: index % 2 !== 0 }),
|
|
||||||
}
|
|
||||||
: result,
|
|
||||||
{}
|
|
||||||
),
|
|
||||||
showContactModal: action('showContactModal'),
|
showContactModal: action('showContactModal'),
|
||||||
startAddingNewMembers: action('startAddingNewMembers'),
|
startAddingNewMembers: action('startAddingNewMembers'),
|
||||||
theme: ThemeType.light,
|
theme: ThemeType.light,
|
||||||
|
|
|
@ -4,14 +4,13 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import type { LocalizerType, ThemeType } from '../../../types/Util';
|
import type { LocalizerType, ThemeType } from '../../../types/Util';
|
||||||
import { getOwn } from '../../../util/getOwn';
|
|
||||||
|
|
||||||
import type { BadgeType } from '../../../badges/types';
|
|
||||||
import { Avatar } from '../../Avatar';
|
import { Avatar } from '../../Avatar';
|
||||||
import { Emojify } from '../Emojify';
|
import { Emojify } from '../Emojify';
|
||||||
|
|
||||||
import { ConversationDetailsIcon, IconType } from './ConversationDetailsIcon';
|
import { ConversationDetailsIcon, IconType } from './ConversationDetailsIcon';
|
||||||
import type { ConversationType } from '../../../state/ducks/conversations';
|
import type { ConversationType } from '../../../state/ducks/conversations';
|
||||||
|
import type { PreferredBadgeSelectorType } from '../../../state/selectors/badges';
|
||||||
import { PanelRow } from './PanelRow';
|
import { PanelRow } from './PanelRow';
|
||||||
import { PanelSection } from './PanelSection';
|
import { PanelSection } from './PanelSection';
|
||||||
|
|
||||||
|
@ -23,10 +22,10 @@ export type GroupV2Membership = {
|
||||||
export type Props = {
|
export type Props = {
|
||||||
canAddNewMembers: boolean;
|
canAddNewMembers: boolean;
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
|
getPreferredBadge: PreferredBadgeSelectorType;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
maxShownMemberCount?: number;
|
maxShownMemberCount?: number;
|
||||||
memberships: Array<GroupV2Membership>;
|
memberships: Array<GroupV2Membership>;
|
||||||
preferredBadgeByConversation: Record<string, BadgeType>;
|
|
||||||
showContactModal: (contactId: string, conversationId: string) => void;
|
showContactModal: (contactId: string, conversationId: string) => void;
|
||||||
startAddingNewMembers?: () => void;
|
startAddingNewMembers?: () => void;
|
||||||
theme: ThemeType;
|
theme: ThemeType;
|
||||||
|
@ -74,10 +73,10 @@ function sortMemberships(
|
||||||
export const ConversationDetailsMembershipList: React.ComponentType<Props> = ({
|
export const ConversationDetailsMembershipList: React.ComponentType<Props> = ({
|
||||||
canAddNewMembers,
|
canAddNewMembers,
|
||||||
conversationId,
|
conversationId,
|
||||||
|
getPreferredBadge,
|
||||||
i18n,
|
i18n,
|
||||||
maxShownMemberCount = 5,
|
maxShownMemberCount = 5,
|
||||||
memberships,
|
memberships,
|
||||||
preferredBadgeByConversation,
|
|
||||||
showContactModal,
|
showContactModal,
|
||||||
startAddingNewMembers,
|
startAddingNewMembers,
|
||||||
theme,
|
theme,
|
||||||
|
@ -114,7 +113,7 @@ export const ConversationDetailsMembershipList: React.ComponentType<Props> = ({
|
||||||
icon={
|
icon={
|
||||||
<Avatar
|
<Avatar
|
||||||
conversationType="direct"
|
conversationType="direct"
|
||||||
badge={getOwn(preferredBadgeByConversation, member.id)}
|
badge={getPreferredBadge(member.badges)}
|
||||||
i18n={i18n}
|
i18n={i18n}
|
||||||
size={32}
|
size={32}
|
||||||
theme={theme}
|
theme={theme}
|
||||||
|
|
|
@ -29,7 +29,6 @@ export const MESSAGE_TEXT_CLASS_NAME = `${MESSAGE_CLASS_NAME}__text`;
|
||||||
const CHECKBOX_CLASS_NAME = `${BASE_CLASS_NAME}__checkbox`;
|
const CHECKBOX_CLASS_NAME = `${BASE_CLASS_NAME}__checkbox`;
|
||||||
|
|
||||||
type PropsType = {
|
type PropsType = {
|
||||||
badge?: BadgeType;
|
|
||||||
checked?: boolean;
|
checked?: boolean;
|
||||||
conversationType: 'group' | 'direct';
|
conversationType: 'group' | 'direct';
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
|
@ -47,7 +46,6 @@ type PropsType = {
|
||||||
messageTextIsAlwaysFullSize?: boolean;
|
messageTextIsAlwaysFullSize?: boolean;
|
||||||
onClick?: () => void;
|
onClick?: () => void;
|
||||||
shouldShowSpinner?: boolean;
|
shouldShowSpinner?: boolean;
|
||||||
theme?: ThemeType;
|
|
||||||
unreadCount?: number;
|
unreadCount?: number;
|
||||||
} & Pick<
|
} & Pick<
|
||||||
ConversationType,
|
ConversationType,
|
||||||
|
@ -62,7 +60,11 @@ type PropsType = {
|
||||||
| 'sharedGroupNames'
|
| 'sharedGroupNames'
|
||||||
| 'title'
|
| 'title'
|
||||||
| 'unblurredAvatarPath'
|
| 'unblurredAvatarPath'
|
||||||
>;
|
> &
|
||||||
|
(
|
||||||
|
| { badge?: undefined; theme?: ThemeType }
|
||||||
|
| { badge: BadgeType; theme: ThemeType }
|
||||||
|
);
|
||||||
|
|
||||||
export const BaseConversationListItem: FunctionComponent<PropsType> =
|
export const BaseConversationListItem: FunctionComponent<PropsType> =
|
||||||
React.memo(function BaseConversationListItem({
|
React.memo(function BaseConversationListItem({
|
||||||
|
|
|
@ -9,7 +9,8 @@ import {
|
||||||
HEADER_CONTACT_NAME_CLASS_NAME,
|
HEADER_CONTACT_NAME_CLASS_NAME,
|
||||||
} from './BaseConversationListItem';
|
} from './BaseConversationListItem';
|
||||||
import type { ConversationType } from '../../state/ducks/conversations';
|
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 { ContactName } from '../conversation/ContactName';
|
||||||
import { About } from '../conversation/About';
|
import { About } from '../conversation/About';
|
||||||
|
|
||||||
|
@ -21,6 +22,7 @@ export enum ContactCheckboxDisabledReason {
|
||||||
}
|
}
|
||||||
|
|
||||||
export type PropsDataType = {
|
export type PropsDataType = {
|
||||||
|
badge: undefined | BadgeType;
|
||||||
disabledReason?: ContactCheckboxDisabledReason;
|
disabledReason?: ContactCheckboxDisabledReason;
|
||||||
isChecked: boolean;
|
isChecked: boolean;
|
||||||
} & Pick<
|
} & Pick<
|
||||||
|
@ -46,6 +48,7 @@ type PropsHousekeepingType = {
|
||||||
id: string,
|
id: string,
|
||||||
disabledReason: undefined | ContactCheckboxDisabledReason
|
disabledReason: undefined | ContactCheckboxDisabledReason
|
||||||
) => void;
|
) => void;
|
||||||
|
theme: ThemeType;
|
||||||
};
|
};
|
||||||
|
|
||||||
type PropsType = PropsDataType & PropsHousekeepingType;
|
type PropsType = PropsDataType & PropsHousekeepingType;
|
||||||
|
@ -55,6 +58,7 @@ export const ContactCheckbox: FunctionComponent<PropsType> = React.memo(
|
||||||
about,
|
about,
|
||||||
acceptedMessageRequest,
|
acceptedMessageRequest,
|
||||||
avatarPath,
|
avatarPath,
|
||||||
|
badge,
|
||||||
color,
|
color,
|
||||||
disabledReason,
|
disabledReason,
|
||||||
i18n,
|
i18n,
|
||||||
|
@ -66,6 +70,7 @@ export const ContactCheckbox: FunctionComponent<PropsType> = React.memo(
|
||||||
phoneNumber,
|
phoneNumber,
|
||||||
profileName,
|
profileName,
|
||||||
sharedGroupNames,
|
sharedGroupNames,
|
||||||
|
theme,
|
||||||
title,
|
title,
|
||||||
type,
|
type,
|
||||||
unblurredAvatarPath,
|
unblurredAvatarPath,
|
||||||
|
@ -97,6 +102,7 @@ export const ContactCheckbox: FunctionComponent<PropsType> = React.memo(
|
||||||
<BaseConversationListItem
|
<BaseConversationListItem
|
||||||
acceptedMessageRequest={acceptedMessageRequest}
|
acceptedMessageRequest={acceptedMessageRequest}
|
||||||
avatarPath={avatarPath}
|
avatarPath={avatarPath}
|
||||||
|
badge={badge}
|
||||||
checked={isChecked}
|
checked={isChecked}
|
||||||
color={color}
|
color={color}
|
||||||
conversationType={type}
|
conversationType={type}
|
||||||
|
@ -112,6 +118,7 @@ export const ContactCheckbox: FunctionComponent<PropsType> = React.memo(
|
||||||
phoneNumber={phoneNumber}
|
phoneNumber={phoneNumber}
|
||||||
profileName={profileName}
|
profileName={profileName}
|
||||||
sharedGroupNames={sharedGroupNames}
|
sharedGroupNames={sharedGroupNames}
|
||||||
|
theme={theme}
|
||||||
title={title}
|
title={title}
|
||||||
unblurredAvatarPath={unblurredAvatarPath}
|
unblurredAvatarPath={unblurredAvatarPath}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -9,15 +9,17 @@ import {
|
||||||
HEADER_CONTACT_NAME_CLASS_NAME,
|
HEADER_CONTACT_NAME_CLASS_NAME,
|
||||||
} from './BaseConversationListItem';
|
} from './BaseConversationListItem';
|
||||||
import type { ConversationType } from '../../state/ducks/conversations';
|
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 { ContactName } from '../conversation/ContactName';
|
||||||
import { About } from '../conversation/About';
|
import { About } from '../conversation/About';
|
||||||
|
|
||||||
export type PropsDataType = Pick<
|
export type ContactListItemConversationType = Pick<
|
||||||
ConversationType,
|
ConversationType,
|
||||||
| 'about'
|
| 'about'
|
||||||
| 'acceptedMessageRequest'
|
| 'acceptedMessageRequest'
|
||||||
| 'avatarPath'
|
| 'avatarPath'
|
||||||
|
| 'badges'
|
||||||
| 'color'
|
| 'color'
|
||||||
| 'id'
|
| 'id'
|
||||||
| 'isMe'
|
| 'isMe'
|
||||||
|
@ -30,9 +32,14 @@ export type PropsDataType = Pick<
|
||||||
| 'unblurredAvatarPath'
|
| 'unblurredAvatarPath'
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
type PropsDataType = ContactListItemConversationType & {
|
||||||
|
badge: undefined | BadgeType;
|
||||||
|
};
|
||||||
|
|
||||||
type PropsHousekeepingType = {
|
type PropsHousekeepingType = {
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
onClick?: (id: string) => void;
|
onClick?: (id: string) => void;
|
||||||
|
theme: ThemeType;
|
||||||
};
|
};
|
||||||
|
|
||||||
type PropsType = PropsDataType & PropsHousekeepingType;
|
type PropsType = PropsDataType & PropsHousekeepingType;
|
||||||
|
@ -42,6 +49,7 @@ export const ContactListItem: FunctionComponent<PropsType> = React.memo(
|
||||||
about,
|
about,
|
||||||
acceptedMessageRequest,
|
acceptedMessageRequest,
|
||||||
avatarPath,
|
avatarPath,
|
||||||
|
badge,
|
||||||
color,
|
color,
|
||||||
i18n,
|
i18n,
|
||||||
id,
|
id,
|
||||||
|
@ -51,6 +59,7 @@ export const ContactListItem: FunctionComponent<PropsType> = React.memo(
|
||||||
phoneNumber,
|
phoneNumber,
|
||||||
profileName,
|
profileName,
|
||||||
sharedGroupNames,
|
sharedGroupNames,
|
||||||
|
theme,
|
||||||
title,
|
title,
|
||||||
type,
|
type,
|
||||||
unblurredAvatarPath,
|
unblurredAvatarPath,
|
||||||
|
@ -70,6 +79,7 @@ export const ContactListItem: FunctionComponent<PropsType> = React.memo(
|
||||||
<BaseConversationListItem
|
<BaseConversationListItem
|
||||||
acceptedMessageRequest={acceptedMessageRequest}
|
acceptedMessageRequest={acceptedMessageRequest}
|
||||||
avatarPath={avatarPath}
|
avatarPath={avatarPath}
|
||||||
|
badge={badge}
|
||||||
color={color}
|
color={color}
|
||||||
conversationType={type}
|
conversationType={type}
|
||||||
headerName={headerName}
|
headerName={headerName}
|
||||||
|
@ -83,6 +93,7 @@ export const ContactListItem: FunctionComponent<PropsType> = React.memo(
|
||||||
phoneNumber={phoneNumber}
|
phoneNumber={phoneNumber}
|
||||||
profileName={profileName}
|
profileName={profileName}
|
||||||
sharedGroupNames={sharedGroupNames}
|
sharedGroupNames={sharedGroupNames}
|
||||||
|
theme={theme}
|
||||||
title={title}
|
title={title}
|
||||||
unblurredAvatarPath={unblurredAvatarPath}
|
unblurredAvatarPath={unblurredAvatarPath}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -8,6 +8,9 @@ import { boolean, text, withKnobs } from '@storybook/addon-knobs';
|
||||||
|
|
||||||
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 { strictAssert } from '../../util/assert';
|
||||||
|
import { getFakeBadge } from '../../test-both/helpers/getFakeBadge';
|
||||||
import type { PropsType } from './MessageSearchResult';
|
import type { PropsType } from './MessageSearchResult';
|
||||||
import { MessageSearchResult } from './MessageSearchResult';
|
import { MessageSearchResult } from './MessageSearchResult';
|
||||||
import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation';
|
import { getDefaultConversation } from '../../test-both/helpers/getDefaultConversation';
|
||||||
|
@ -37,7 +40,7 @@ const group = getDefaultConversation({
|
||||||
type: 'group',
|
type: 'group',
|
||||||
});
|
});
|
||||||
|
|
||||||
const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
const useProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||||
i18n,
|
i18n,
|
||||||
id: '',
|
id: '',
|
||||||
conversationId: '',
|
conversationId: '',
|
||||||
|
@ -50,16 +53,18 @@ const createProps = (overrideProps: Partial<PropsType> = {}): PropsType => ({
|
||||||
bodyRanges: overrideProps.bodyRanges || [],
|
bodyRanges: overrideProps.bodyRanges || [],
|
||||||
from: overrideProps.from as PropsType['from'],
|
from: overrideProps.from as PropsType['from'],
|
||||||
to: overrideProps.to as PropsType['to'],
|
to: overrideProps.to as PropsType['to'],
|
||||||
|
getPreferredBadge: overrideProps.getPreferredBadge || (() => undefined),
|
||||||
isSelected: boolean('isSelected', overrideProps.isSelected || false),
|
isSelected: boolean('isSelected', overrideProps.isSelected || false),
|
||||||
openConversationInternal: action('openConversationInternal'),
|
openConversationInternal: action('openConversationInternal'),
|
||||||
isSearchingInConversation: boolean(
|
isSearchingInConversation: boolean(
|
||||||
'isSearchingInConversation',
|
'isSearchingInConversation',
|
||||||
overrideProps.isSearchingInConversation || false
|
overrideProps.isSearchingInConversation || false
|
||||||
),
|
),
|
||||||
|
theme: React.useContext(StorybookThemeContext),
|
||||||
});
|
});
|
||||||
|
|
||||||
story.add('Default', () => {
|
story.add('Default', () => {
|
||||||
const props = createProps({
|
const props = useProps({
|
||||||
from: someone,
|
from: someone,
|
||||||
to: me,
|
to: me,
|
||||||
});
|
});
|
||||||
|
@ -67,8 +72,24 @@ story.add('Default', () => {
|
||||||
return <MessageSearchResult {...props} />;
|
return <MessageSearchResult {...props} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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 <MessageSearchResult {...props} />;
|
||||||
|
});
|
||||||
|
|
||||||
story.add('Selected', () => {
|
story.add('Selected', () => {
|
||||||
const props = createProps({
|
const props = useProps({
|
||||||
from: someone,
|
from: someone,
|
||||||
to: me,
|
to: me,
|
||||||
isSelected: true,
|
isSelected: true,
|
||||||
|
@ -78,7 +99,7 @@ story.add('Selected', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
story.add('From You', () => {
|
story.add('From You', () => {
|
||||||
const props = createProps({
|
const props = useProps({
|
||||||
from: me,
|
from: me,
|
||||||
to: someone,
|
to: someone,
|
||||||
});
|
});
|
||||||
|
@ -87,7 +108,7 @@ story.add('From You', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
story.add('Searching in Conversation', () => {
|
story.add('Searching in Conversation', () => {
|
||||||
const props = createProps({
|
const props = useProps({
|
||||||
from: me,
|
from: me,
|
||||||
to: someone,
|
to: someone,
|
||||||
isSearchingInConversation: true,
|
isSearchingInConversation: true,
|
||||||
|
@ -97,7 +118,7 @@ story.add('Searching in Conversation', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
story.add('From You to Yourself', () => {
|
story.add('From You to Yourself', () => {
|
||||||
const props = createProps({
|
const props = useProps({
|
||||||
from: me,
|
from: me,
|
||||||
to: me,
|
to: me,
|
||||||
});
|
});
|
||||||
|
@ -106,7 +127,7 @@ story.add('From You to Yourself', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
story.add('From You to Group', () => {
|
story.add('From You to Group', () => {
|
||||||
const props = createProps({
|
const props = useProps({
|
||||||
from: me,
|
from: me,
|
||||||
to: group,
|
to: group,
|
||||||
});
|
});
|
||||||
|
@ -115,7 +136,7 @@ story.add('From You to Group', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
story.add('From Someone to Group', () => {
|
story.add('From Someone to Group', () => {
|
||||||
const props = createProps({
|
const props = useProps({
|
||||||
from: someone,
|
from: someone,
|
||||||
to: group,
|
to: group,
|
||||||
});
|
});
|
||||||
|
@ -130,7 +151,7 @@ story.add('Long Search Result', () => {
|
||||||
];
|
];
|
||||||
|
|
||||||
return snippets.map(snippet => {
|
return snippets.map(snippet => {
|
||||||
const props = createProps({
|
const props = useProps({
|
||||||
from: someone,
|
from: someone,
|
||||||
to: me,
|
to: me,
|
||||||
snippet,
|
snippet,
|
||||||
|
@ -141,13 +162,13 @@ story.add('Long Search Result', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
story.add('Empty (should be invalid)', () => {
|
story.add('Empty (should be invalid)', () => {
|
||||||
const props = createProps();
|
const props = useProps();
|
||||||
|
|
||||||
return <MessageSearchResult {...props} />;
|
return <MessageSearchResult {...props} />;
|
||||||
});
|
});
|
||||||
|
|
||||||
story.add('@mention', () => {
|
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',
|
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: [
|
bodyRanges: [
|
||||||
{
|
{
|
||||||
|
@ -173,7 +194,7 @@ story.add('@mention', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
story.add('@mention regexp', () => {
|
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',
|
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: [
|
bodyRanges: [
|
||||||
{
|
{
|
||||||
|
@ -193,7 +214,7 @@ story.add('@mention regexp', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
story.add('@mention no-matches', () => {
|
story.add('@mention no-matches', () => {
|
||||||
const props = createProps({
|
const props = useProps({
|
||||||
body: '\uFFFC hello',
|
body: '\uFFFC hello',
|
||||||
bodyRanges: [
|
bodyRanges: [
|
||||||
{
|
{
|
||||||
|
@ -212,7 +233,7 @@ story.add('@mention no-matches', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
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',
|
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: [
|
bodyRanges: [
|
||||||
{
|
{
|
||||||
|
@ -238,7 +259,7 @@ story.add('@mention no-matches', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
story.add('Double @mention', () => {
|
story.add('Double @mention', () => {
|
||||||
const props = createProps({
|
const props = useProps({
|
||||||
body: 'Hey \uFFFC \uFFFC test',
|
body: 'Hey \uFFFC \uFFFC test',
|
||||||
bodyRanges: [
|
bodyRanges: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,9 +9,14 @@ import { MessageBodyHighlight } from './MessageBodyHighlight';
|
||||||
import { ContactName } from '../conversation/ContactName';
|
import { ContactName } from '../conversation/ContactName';
|
||||||
|
|
||||||
import { assert } from '../../util/assert';
|
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 { BaseConversationListItem } from './BaseConversationListItem';
|
||||||
import type { ConversationType } from '../../state/ducks/conversations';
|
import type { ConversationType } from '../../state/ducks/conversations';
|
||||||
|
import type { PreferredBadgeSelectorType } from '../../state/selectors/badges';
|
||||||
|
|
||||||
export type PropsDataType = {
|
export type PropsDataType = {
|
||||||
isSelected?: boolean;
|
isSelected?: boolean;
|
||||||
|
@ -29,6 +34,7 @@ export type PropsDataType = {
|
||||||
ConversationType,
|
ConversationType,
|
||||||
| 'acceptedMessageRequest'
|
| 'acceptedMessageRequest'
|
||||||
| 'avatarPath'
|
| 'avatarPath'
|
||||||
|
| 'badges'
|
||||||
| 'color'
|
| 'color'
|
||||||
| 'isMe'
|
| 'isMe'
|
||||||
| 'name'
|
| 'name'
|
||||||
|
@ -50,11 +56,13 @@ export type PropsDataType = {
|
||||||
};
|
};
|
||||||
|
|
||||||
type PropsHousekeepingType = {
|
type PropsHousekeepingType = {
|
||||||
|
getPreferredBadge: PreferredBadgeSelectorType;
|
||||||
i18n: LocalizerType;
|
i18n: LocalizerType;
|
||||||
openConversationInternal: (_: {
|
openConversationInternal: (_: {
|
||||||
conversationId: string;
|
conversationId: string;
|
||||||
messageId?: string;
|
messageId?: string;
|
||||||
}) => void;
|
}) => void;
|
||||||
|
theme: ThemeType;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PropsType = PropsDataType & PropsHousekeepingType;
|
export type PropsType = PropsDataType & PropsHousekeepingType;
|
||||||
|
@ -136,11 +144,13 @@ export const MessageSearchResult: FunctionComponent<PropsType> = React.memo(
|
||||||
bodyRanges,
|
bodyRanges,
|
||||||
conversationId,
|
conversationId,
|
||||||
from,
|
from,
|
||||||
|
getPreferredBadge,
|
||||||
i18n,
|
i18n,
|
||||||
id,
|
id,
|
||||||
openConversationInternal,
|
openConversationInternal,
|
||||||
sentAt,
|
sentAt,
|
||||||
snippet,
|
snippet,
|
||||||
|
theme,
|
||||||
to,
|
to,
|
||||||
}) {
|
}) {
|
||||||
const onClickItem = useCallback(() => {
|
const onClickItem = useCallback(() => {
|
||||||
|
@ -179,6 +189,7 @@ export const MessageSearchResult: FunctionComponent<PropsType> = React.memo(
|
||||||
<BaseConversationListItem
|
<BaseConversationListItem
|
||||||
acceptedMessageRequest={from.acceptedMessageRequest}
|
acceptedMessageRequest={from.acceptedMessageRequest}
|
||||||
avatarPath={from.avatarPath}
|
avatarPath={from.avatarPath}
|
||||||
|
badge={getPreferredBadge(from.badges)}
|
||||||
color={from.color}
|
color={from.color}
|
||||||
conversationType="direct"
|
conversationType="direct"
|
||||||
headerDate={sentAt}
|
headerDate={sentAt}
|
||||||
|
@ -194,6 +205,7 @@ export const MessageSearchResult: FunctionComponent<PropsType> = React.memo(
|
||||||
phoneNumber={from.phoneNumber}
|
phoneNumber={from.phoneNumber}
|
||||||
profileName={from.profileName}
|
profileName={from.profileName}
|
||||||
sharedGroupNames={from.sharedGroupNames}
|
sharedGroupNames={from.sharedGroupNames}
|
||||||
|
theme={theme}
|
||||||
title={from.title}
|
title={from.title}
|
||||||
unblurredAvatarPath={from.unblurredAvatarPath}
|
unblurredAvatarPath={from.unblurredAvatarPath}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -8,7 +8,7 @@ import type { PhoneNumber } from 'google-libphonenumber';
|
||||||
import { LeftPaneHelper } from './LeftPaneHelper';
|
import { LeftPaneHelper } from './LeftPaneHelper';
|
||||||
import type { Row } from '../ConversationList';
|
import type { Row } from '../ConversationList';
|
||||||
import { RowType } 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 type { PropsData as ConversationListItemPropsType } from '../conversationList/ConversationListItem';
|
||||||
import { SearchInput } from '../SearchInput';
|
import { SearchInput } from '../SearchInput';
|
||||||
import type { LocalizerType } from '../../types/Util';
|
import type { LocalizerType } from '../../types/Util';
|
||||||
|
@ -21,7 +21,7 @@ import { missingCaseError } from '../../util/missingCaseError';
|
||||||
import { getUsernameFromSearch } from '../../types/Username';
|
import { getUsernameFromSearch } from '../../types/Username';
|
||||||
|
|
||||||
export type LeftPaneComposePropsType = {
|
export type LeftPaneComposePropsType = {
|
||||||
composeContacts: ReadonlyArray<ContactListItemPropsType>;
|
composeContacts: ReadonlyArray<ContactListItemConversationType>;
|
||||||
composeGroups: ReadonlyArray<ConversationListItemPropsType>;
|
composeGroups: ReadonlyArray<ConversationListItemPropsType>;
|
||||||
|
|
||||||
regionCode: string;
|
regionCode: string;
|
||||||
|
@ -37,7 +37,7 @@ enum TopButton {
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LeftPaneComposeHelper extends LeftPaneHelper<LeftPaneComposePropsType> {
|
export class LeftPaneComposeHelper extends LeftPaneHelper<LeftPaneComposePropsType> {
|
||||||
private readonly composeContacts: ReadonlyArray<ContactListItemPropsType>;
|
private readonly composeContacts: ReadonlyArray<ContactListItemConversationType>;
|
||||||
|
|
||||||
private readonly composeGroups: ReadonlyArray<ConversationListItemPropsType>;
|
private readonly composeGroups: ReadonlyArray<ConversationListItemPropsType>;
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import React from 'react';
|
||||||
import { LeftPaneHelper } from './LeftPaneHelper';
|
import { LeftPaneHelper } from './LeftPaneHelper';
|
||||||
import type { Row } from '../ConversationList';
|
import type { Row } from '../ConversationList';
|
||||||
import { RowType } 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 { DisappearingTimerSelect } from '../DisappearingTimerSelect';
|
||||||
import type { LocalizerType } from '../../types/Util';
|
import type { LocalizerType } from '../../types/Util';
|
||||||
import { Alert } from '../Alert';
|
import { Alert } from '../Alert';
|
||||||
|
@ -32,7 +32,7 @@ export type LeftPaneSetGroupMetadataPropsType = {
|
||||||
hasError: boolean;
|
hasError: boolean;
|
||||||
isCreating: boolean;
|
isCreating: boolean;
|
||||||
isEditingAvatar: boolean;
|
isEditingAvatar: boolean;
|
||||||
selectedContacts: ReadonlyArray<ContactListItemPropsType>;
|
selectedContacts: ReadonlyArray<ContactListItemConversationType>;
|
||||||
userAvatarData: ReadonlyArray<AvatarDataType>;
|
userAvatarData: ReadonlyArray<AvatarDataType>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ export class LeftPaneSetGroupMetadataHelper extends LeftPaneHelper<LeftPaneSetGr
|
||||||
|
|
||||||
private readonly isEditingAvatar: boolean;
|
private readonly isEditingAvatar: boolean;
|
||||||
|
|
||||||
private readonly selectedContacts: ReadonlyArray<ContactListItemPropsType>;
|
private readonly selectedContacts: ReadonlyArray<ContactListItemConversationType>;
|
||||||
|
|
||||||
private readonly userAvatarData: ReadonlyArray<AvatarDataType>;
|
private readonly userAvatarData: ReadonlyArray<AvatarDataType>;
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,6 @@ import {
|
||||||
getBadgesSelector,
|
getBadgesSelector,
|
||||||
getPreferredBadgeSelector,
|
getPreferredBadgeSelector,
|
||||||
} from '../selectors/badges';
|
} from '../selectors/badges';
|
||||||
import type { BadgeType } from '../../badges/types';
|
|
||||||
import { assert } from '../../util/assert';
|
import { assert } from '../../util/assert';
|
||||||
import { SignalService as Proto } from '../../protobuf';
|
import { SignalService as Proto } from '../../protobuf';
|
||||||
|
|
||||||
|
@ -81,24 +80,15 @@ const mapStateToProps = (
|
||||||
|
|
||||||
const badges = getBadgesSelector(state)(conversation.badges);
|
const badges = getBadgesSelector(state)(conversation.badges);
|
||||||
|
|
||||||
const preferredBadgeByConversation: Record<string, BadgeType> = {};
|
|
||||||
const getPreferredBadge = getPreferredBadgeSelector(state);
|
|
||||||
groupMemberships.memberships.forEach(({ member }) => {
|
|
||||||
const preferredBadge = getPreferredBadge(member.badges);
|
|
||||||
if (preferredBadge) {
|
|
||||||
preferredBadgeByConversation[member.id] = preferredBadge;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...props,
|
...props,
|
||||||
badges,
|
badges,
|
||||||
canEditGroupInfo,
|
canEditGroupInfo,
|
||||||
candidateContactsToAdd,
|
candidateContactsToAdd,
|
||||||
conversation,
|
conversation,
|
||||||
|
getPreferredBadge: getPreferredBadgeSelector(state),
|
||||||
i18n: getIntl(state),
|
i18n: getIntl(state),
|
||||||
isAdmin,
|
isAdmin,
|
||||||
preferredBadgeByConversation,
|
|
||||||
...groupMemberships,
|
...groupMemberships,
|
||||||
userAvatarData: conversation.avatars || [],
|
userAvatarData: conversation.avatars || [],
|
||||||
hasGroupLink,
|
hasGroupLink,
|
||||||
|
|
|
@ -20,7 +20,7 @@ import {
|
||||||
isSearching,
|
isSearching,
|
||||||
} from '../selectors/search';
|
} from '../selectors/search';
|
||||||
import { getIntl, getRegionCode, getTheme } from '../selectors/user';
|
import { getIntl, getRegionCode, getTheme } from '../selectors/user';
|
||||||
import { getBadgesById } from '../selectors/badges';
|
import { getPreferredBadgeSelector } from '../selectors/badges';
|
||||||
import {
|
import {
|
||||||
getPreferredLeftPaneWidth,
|
getPreferredLeftPaneWidth,
|
||||||
getUsernamesEnabled,
|
getUsernamesEnabled,
|
||||||
|
@ -166,7 +166,6 @@ const getModeSpecificProps = (
|
||||||
const mapStateToProps = (state: StateType) => {
|
const mapStateToProps = (state: StateType) => {
|
||||||
return {
|
return {
|
||||||
modeSpecificProps: getModeSpecificProps(state),
|
modeSpecificProps: getModeSpecificProps(state),
|
||||||
badgesById: getBadgesById(state),
|
|
||||||
canResizeLeftPane: window.Signal.RemoteConfig.isEnabled(
|
canResizeLeftPane: window.Signal.RemoteConfig.isEnabled(
|
||||||
'desktop.internalUser'
|
'desktop.internalUser'
|
||||||
),
|
),
|
||||||
|
@ -174,6 +173,7 @@ const mapStateToProps = (state: StateType) => {
|
||||||
selectedConversationId: getSelectedConversationId(state),
|
selectedConversationId: getSelectedConversationId(state),
|
||||||
selectedMessageId: getSelectedMessage(state)?.id,
|
selectedMessageId: getSelectedMessage(state)?.id,
|
||||||
showArchived: getShowArchived(state),
|
showArchived: getShowArchived(state),
|
||||||
|
getPreferredBadge: getPreferredBadgeSelector(state),
|
||||||
i18n: getIntl(state),
|
i18n: getIntl(state),
|
||||||
regionCode: getRegionCode(state),
|
regionCode: getRegionCode(state),
|
||||||
challengeStatus: state.network.challengeStatus,
|
challengeStatus: state.network.challengeStatus,
|
||||||
|
|
|
@ -8,7 +8,8 @@ import { mapDispatchToProps } from '../actions';
|
||||||
import type { StateType } from '../reducer';
|
import type { StateType } from '../reducer';
|
||||||
|
|
||||||
import { MessageSearchResult } from '../../components/conversationList/MessageSearchResult';
|
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';
|
import { getMessageSearchResultSelector } from '../selectors/search';
|
||||||
|
|
||||||
type SmartProps = {
|
type SmartProps = {
|
||||||
|
@ -26,8 +27,10 @@ function mapStateToProps(state: StateType, ourProps: SmartProps) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...props,
|
...props,
|
||||||
|
getPreferredBadge: getPreferredBadgeSelector(state),
|
||||||
i18n: getIntl(state),
|
i18n: getIntl(state),
|
||||||
style,
|
style,
|
||||||
|
theme: getTheme(state),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const smart = connect(mapStateToProps, mapDispatchToProps);
|
const smart = connect(mapStateToProps, mapDispatchToProps);
|
||||||
|
|
Loading…
Add table
Reference in a new issue