142 lines
4.2 KiB
TypeScript
142 lines
4.2 KiB
TypeScript
// Copyright 2021 Signal Messenger, LLC
|
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
import React from 'react';
|
|
|
|
import type { LocalizerType, ThemeType } from '../../../types/Util';
|
|
|
|
import { Avatar, AvatarSize } 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';
|
|
|
|
export type GroupV2Membership = {
|
|
isAdmin: boolean;
|
|
member: ConversationType;
|
|
};
|
|
|
|
export type Props = {
|
|
canAddNewMembers: boolean;
|
|
conversationId: string;
|
|
getPreferredBadge: PreferredBadgeSelectorType;
|
|
i18n: LocalizerType;
|
|
maxShownMemberCount?: number;
|
|
memberships: ReadonlyArray<GroupV2Membership>;
|
|
showContactModal: (contactId: string, conversationId?: string) => void;
|
|
startAddingNewMembers?: () => void;
|
|
theme: ThemeType;
|
|
};
|
|
|
|
const collator = new Intl.Collator(undefined, { sensitivity: 'base' });
|
|
function sortConversationTitles(
|
|
left: GroupV2Membership,
|
|
right: GroupV2Membership
|
|
) {
|
|
const leftTitle = left.member.title;
|
|
const rightTitle = right.member.title;
|
|
return collator.compare(leftTitle, rightTitle);
|
|
}
|
|
|
|
function sortMemberships(
|
|
memberships: ReadonlyArray<GroupV2Membership>
|
|
): Array<GroupV2Membership> {
|
|
let you: undefined | GroupV2Membership;
|
|
const admins: Array<GroupV2Membership> = [];
|
|
const nonAdmins: Array<GroupV2Membership> = [];
|
|
memberships.forEach(membershipInfo => {
|
|
const { isAdmin, member } = membershipInfo;
|
|
if (member.isMe) {
|
|
you = membershipInfo;
|
|
} else if (isAdmin) {
|
|
admins.push(membershipInfo);
|
|
} else {
|
|
nonAdmins.push(membershipInfo);
|
|
}
|
|
});
|
|
admins.sort(sortConversationTitles);
|
|
nonAdmins.sort(sortConversationTitles);
|
|
|
|
const sortedMemberships = [];
|
|
if (you) {
|
|
sortedMemberships.push(you);
|
|
}
|
|
sortedMemberships.push(...admins);
|
|
sortedMemberships.push(...nonAdmins);
|
|
|
|
return sortedMemberships;
|
|
}
|
|
|
|
export function ConversationDetailsMembershipList({
|
|
canAddNewMembers,
|
|
conversationId,
|
|
getPreferredBadge,
|
|
i18n,
|
|
maxShownMemberCount = 5,
|
|
memberships,
|
|
showContactModal,
|
|
startAddingNewMembers,
|
|
theme,
|
|
}: Props): JSX.Element {
|
|
const [showAllMembers, setShowAllMembers] = React.useState<boolean>(false);
|
|
const sortedMemberships = sortMemberships(memberships);
|
|
|
|
const shouldHideRestMembers =
|
|
sortedMemberships.length - maxShownMemberCount > 1;
|
|
const membersToShow =
|
|
shouldHideRestMembers && !showAllMembers
|
|
? maxShownMemberCount
|
|
: sortedMemberships.length;
|
|
|
|
return (
|
|
<PanelSection
|
|
title={i18n('ConversationDetailsMembershipList--title', [
|
|
sortedMemberships.length.toString(),
|
|
])}
|
|
>
|
|
{canAddNewMembers && (
|
|
<PanelRow
|
|
icon={
|
|
<div className="ConversationDetails-membership-list__add-members-icon" />
|
|
}
|
|
label={i18n('ConversationDetailsMembershipList--add-members')}
|
|
onClick={() => startAddingNewMembers?.()}
|
|
/>
|
|
)}
|
|
{sortedMemberships.slice(0, membersToShow).map(({ isAdmin, member }) => (
|
|
<PanelRow
|
|
key={member.id}
|
|
onClick={() => showContactModal(member.id, conversationId)}
|
|
icon={
|
|
<Avatar
|
|
conversationType="direct"
|
|
badge={getPreferredBadge(member.badges)}
|
|
i18n={i18n}
|
|
size={AvatarSize.THIRTY_TWO}
|
|
theme={theme}
|
|
{...member}
|
|
/>
|
|
}
|
|
label={<Emojify text={member.isMe ? i18n('you') : member.title} />}
|
|
right={isAdmin ? i18n('GroupV2--admin') : ''}
|
|
/>
|
|
))}
|
|
{showAllMembers === false && shouldHideRestMembers && (
|
|
<PanelRow
|
|
className="ConversationDetails-membership-list--show-all"
|
|
icon={
|
|
<ConversationDetailsIcon
|
|
ariaLabel={i18n('ConversationDetailsMembershipList--show-all')}
|
|
icon={IconType.down}
|
|
/>
|
|
}
|
|
onClick={() => setShowAllMembers(true)}
|
|
label={i18n('ConversationDetailsMembershipList--show-all')}
|
|
/>
|
|
)}
|
|
</PanelSection>
|
|
);
|
|
}
|