Implement changes to memberlist from feedback (#29029)
* Add a separator between joined and invited members * Fix user label in tile having wrong color * Changes to member tiles - ThreePidInviteTile now contains an user label showing "(Invited)" and an email icon. - RoomMemberTile now includes an icon similar to above. - Refactors a bunch of code to make this change sensible. * Remove redundant css code * Fix tests * Update src/components/viewmodels/memberlist/MemberListViewModel.tsx Co-authored-by: Michael Telatynski <7t3chguy@gmail.com> * Update year in license * Fix lint error --------- Co-authored-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
co-authored by
Michael Telatynski
parent
cf895b4296
commit
1644169ff3
Binary file not shown.
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
@@ -283,6 +283,7 @@
|
|||||||
@import "./views/rooms/_EventTile.pcss";
|
@import "./views/rooms/_EventTile.pcss";
|
||||||
@import "./views/rooms/_HistoryTile.pcss";
|
@import "./views/rooms/_HistoryTile.pcss";
|
||||||
@import "./views/rooms/_IRCLayout.pcss";
|
@import "./views/rooms/_IRCLayout.pcss";
|
||||||
|
@import "./views/rooms/_InvitedIconView.pcss";
|
||||||
@import "./views/rooms/_JumpToBottomButton.pcss";
|
@import "./views/rooms/_JumpToBottomButton.pcss";
|
||||||
@import "./views/rooms/_LinkPreviewGroup.pcss";
|
@import "./views/rooms/_LinkPreviewGroup.pcss";
|
||||||
@import "./views/rooms/_LinkPreviewWidget.pcss";
|
@import "./views/rooms/_LinkPreviewWidget.pcss";
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2025 New Vector Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_InvitedIconView {
|
||||||
|
color: var(--cpd-color-icon-tertiary);
|
||||||
|
}
|
||||||
@@ -14,4 +14,10 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
.mx_MemberListView_container {
|
.mx_MemberListView_container {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.mx_MemberListView_separator {
|
||||||
|
margin: 0;
|
||||||
|
border: none;
|
||||||
|
border-top: 2px solid var(--cpd-color-bg-subtle-primary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,9 +31,10 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MemberTileView_user_label {
|
.mx_MemberTileView_userLabel {
|
||||||
font: var(--cpd-font-body-sm-regular);
|
font: var(--cpd-font-body-sm-regular);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
color: var(--cpd-color-text-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_MemberTileView_avatar {
|
.mx_MemberTileView_avatar {
|
||||||
@@ -41,18 +42,4 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
height: 32px;
|
height: 32px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_E2EIconView {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_E2EIconView_warning {
|
|
||||||
color: var(--cpd-color-icon-critical-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.mx_E2EIconView_verified {
|
|
||||||
color: var(--cpd-color-icon-success-primary);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,8 +99,12 @@ export function sdkRoomMemberToRoomMember(member: SdkRoomMember): Member {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const SEPARATOR = "SEPARATOR";
|
||||||
|
export type MemberWithSeparator = Member | typeof SEPARATOR;
|
||||||
|
|
||||||
export interface MemberListViewState {
|
export interface MemberListViewState {
|
||||||
members: Member[];
|
members: MemberWithSeparator[];
|
||||||
|
memberCount: number;
|
||||||
search: (searchQuery: string) => void;
|
search: (searchQuery: string) => void;
|
||||||
isPresenceEnabled: boolean;
|
isPresenceEnabled: boolean;
|
||||||
shouldShowInvite: boolean;
|
shouldShowInvite: boolean;
|
||||||
@@ -118,10 +122,16 @@ export function useMemberListViewModel(roomId: string): MemberListViewState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const sdkContext = useContext(SDKContext);
|
const sdkContext = useContext(SDKContext);
|
||||||
const [memberMap, setMemberMap] = useState<Map<string, Member>>(new Map());
|
const [memberMap, setMemberMap] = useState<Map<string, MemberWithSeparator>>(new Map());
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(true);
|
const [isLoading, setIsLoading] = useState<boolean>(true);
|
||||||
// This is the last known total number of members in this room.
|
// This is the last known total number of members in this room.
|
||||||
const [totalMemberCount, setTotalMemberCount] = useState(0);
|
const [totalMemberCount, setTotalMemberCount] = useState(0);
|
||||||
|
/**
|
||||||
|
* This is the current number of members in the list.
|
||||||
|
* This number will be less than the total number of members
|
||||||
|
* in the room when the search functionality is used.
|
||||||
|
*/
|
||||||
|
const [memberCount, setMemberCount] = useState(0);
|
||||||
|
|
||||||
const loadMembers = useMemo(
|
const loadMembers = useMemo(
|
||||||
() =>
|
() =>
|
||||||
@@ -131,24 +141,34 @@ export function useMemberListViewModel(roomId: string): MemberListViewState {
|
|||||||
roomId,
|
roomId,
|
||||||
searchQuery,
|
searchQuery,
|
||||||
);
|
);
|
||||||
const newMemberMap = new Map<string, Member>();
|
|
||||||
// First add the invited room members
|
|
||||||
for (const member of invitedSdk) {
|
|
||||||
const roomMember = sdkRoomMemberToRoomMember(member);
|
|
||||||
newMemberMap.set(member.userId, roomMember);
|
|
||||||
}
|
|
||||||
// Then add the third party invites
|
|
||||||
const threePidInvited = getPending3PidInvites(room, searchQuery);
|
const threePidInvited = getPending3PidInvites(room, searchQuery);
|
||||||
for (const invited of threePidInvited) {
|
|
||||||
const key = invited.threePidInvite!.event.getContent().display_name;
|
const newMemberMap = new Map<string, MemberWithSeparator>();
|
||||||
newMemberMap.set(key, invited);
|
|
||||||
}
|
// First add the joined room members
|
||||||
// Finally add the joined room members
|
|
||||||
for (const member of joinedSdk) {
|
for (const member of joinedSdk) {
|
||||||
const roomMember = sdkRoomMemberToRoomMember(member);
|
const roomMember = sdkRoomMemberToRoomMember(member);
|
||||||
newMemberMap.set(member.userId, roomMember);
|
newMemberMap.set(member.userId, roomMember);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Then a separator if needed
|
||||||
|
if (joinedSdk.length > 0 && (invitedSdk.length > 0 || threePidInvited.length > 0))
|
||||||
|
newMemberMap.set(SEPARATOR, SEPARATOR);
|
||||||
|
|
||||||
|
// Then add the invited room members
|
||||||
|
for (const member of invitedSdk) {
|
||||||
|
const roomMember = sdkRoomMemberToRoomMember(member);
|
||||||
|
newMemberMap.set(member.userId, roomMember);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally add the third party invites
|
||||||
|
for (const invited of threePidInvited) {
|
||||||
|
const key = invited.threePidInvite!.event.getContent().display_name;
|
||||||
|
newMemberMap.set(key, invited);
|
||||||
|
}
|
||||||
|
|
||||||
setMemberMap(newMemberMap);
|
setMemberMap(newMemberMap);
|
||||||
|
setMemberCount(joinedSdk.length + invitedSdk.length + threePidInvited.length);
|
||||||
if (!searchQuery) {
|
if (!searchQuery) {
|
||||||
/**
|
/**
|
||||||
* Since searching for members only gives you the relevant
|
* Since searching for members only gives you the relevant
|
||||||
@@ -241,6 +261,7 @@ export function useMemberListViewModel(roomId: string): MemberListViewState {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
members: Array.from(memberMap.values()),
|
members: Array.from(memberMap.values()),
|
||||||
|
memberCount,
|
||||||
search: loadMembers,
|
search: loadMembers,
|
||||||
shouldShowInvite,
|
shouldShowInvite,
|
||||||
isPresenceEnabled,
|
isPresenceEnabled,
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ Please see LICENSE files in the repository root for full details.
|
|||||||
import dis from "../../../../dispatcher/dispatcher";
|
import dis from "../../../../dispatcher/dispatcher";
|
||||||
import { Action } from "../../../../dispatcher/actions";
|
import { Action } from "../../../../dispatcher/actions";
|
||||||
import { ThreePIDInvite } from "../../../../models/rooms/ThreePIDInvite";
|
import { ThreePIDInvite } from "../../../../models/rooms/ThreePIDInvite";
|
||||||
|
import { _t } from "../../../../languageHandler";
|
||||||
|
|
||||||
interface ThreePidTileViewModelProps {
|
interface ThreePidTileViewModelProps {
|
||||||
threePidInvite: ThreePIDInvite;
|
threePidInvite: ThreePIDInvite;
|
||||||
@@ -16,6 +17,7 @@ interface ThreePidTileViewModelProps {
|
|||||||
export interface ThreePidTileViewState {
|
export interface ThreePidTileViewState {
|
||||||
name: string;
|
name: string;
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
|
userLabel?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useThreePidTileViewModel(props: ThreePidTileViewModelProps): ThreePidTileViewState {
|
export function useThreePidTileViewModel(props: ThreePidTileViewModelProps): ThreePidTileViewState {
|
||||||
@@ -28,8 +30,11 @@ export function useThreePidTileViewModel(props: ThreePidTileViewModelProps): Thr
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const userLabel = `(${_t("member_list|invited_label")})`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
name,
|
name,
|
||||||
onClick,
|
onClick,
|
||||||
|
userLabel,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,12 +88,10 @@ function getHeaderLabelJSX(vm: MemberListViewState): React.ReactNode {
|
|||||||
</Flex>
|
</Flex>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if (vm.memberCount === 0) {
|
||||||
const filteredMemberCount = vm.members.length;
|
|
||||||
if (filteredMemberCount === 0) {
|
|
||||||
return _t("member_list|no_matches");
|
return _t("member_list|no_matches");
|
||||||
}
|
}
|
||||||
return _t("member_list|count", { count: filteredMemberCount });
|
return _t("member_list|count", { count: vm.memberCount });
|
||||||
}
|
}
|
||||||
|
|
||||||
export const MemberListHeaderView: React.FC<Props> = (props: Props) => {
|
export const MemberListHeaderView: React.FC<Props> = (props: Props) => {
|
||||||
|
|||||||
@@ -11,7 +11,11 @@ import { List, ListRowProps } from "react-virtualized/dist/commonjs/List";
|
|||||||
import { AutoSizer } from "react-virtualized";
|
import { AutoSizer } from "react-virtualized";
|
||||||
|
|
||||||
import { Flex } from "../../../utils/Flex";
|
import { Flex } from "../../../utils/Flex";
|
||||||
import { useMemberListViewModel } from "../../../viewmodels/memberlist/MemberListViewModel";
|
import {
|
||||||
|
MemberWithSeparator,
|
||||||
|
SEPARATOR,
|
||||||
|
useMemberListViewModel,
|
||||||
|
} from "../../../viewmodels/memberlist/MemberListViewModel";
|
||||||
import { RoomMemberTileView } from "./tiles/RoomMemberTileView";
|
import { RoomMemberTileView } from "./tiles/RoomMemberTileView";
|
||||||
import { ThreePidInviteTileView } from "./tiles/ThreePidInviteTileView";
|
import { ThreePidInviteTileView } from "./tiles/ThreePidInviteTileView";
|
||||||
import { MemberListHeaderView } from "./MemberListHeaderView";
|
import { MemberListHeaderView } from "./MemberListHeaderView";
|
||||||
@@ -26,10 +30,41 @@ interface IProps {
|
|||||||
const MemberListView: React.FC<IProps> = (props: IProps) => {
|
const MemberListView: React.FC<IProps> = (props: IProps) => {
|
||||||
const vm = useMemberListViewModel(props.roomId);
|
const vm = useMemberListViewModel(props.roomId);
|
||||||
|
|
||||||
const memberCount = vm.members.length;
|
const totalRows = vm.members.length;
|
||||||
|
|
||||||
|
const getRowComponent = (item: MemberWithSeparator): React.JSX.Element => {
|
||||||
|
if (item === SEPARATOR) {
|
||||||
|
return <hr className="mx_MemberListView_separator" />;
|
||||||
|
} else if (item.member) {
|
||||||
|
return <RoomMemberTileView member={item.member} showPresence={vm.isPresenceEnabled} />;
|
||||||
|
} else {
|
||||||
|
return <ThreePidInviteTileView threePidInvite={item.threePidInvite} />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRowHeight = ({ index }: { index: number }): number => {
|
||||||
|
if (vm.members[index] === SEPARATOR) {
|
||||||
|
/**
|
||||||
|
* This is a separator of 2px height rendered between
|
||||||
|
* joined and invited members.
|
||||||
|
*/
|
||||||
|
return 2;
|
||||||
|
} else if (totalRows && index === totalRows) {
|
||||||
|
/**
|
||||||
|
* The empty spacer div rendered at the bottom should
|
||||||
|
* have a height of 32px.
|
||||||
|
*/
|
||||||
|
return 32;
|
||||||
|
} else {
|
||||||
|
/**
|
||||||
|
* The actual member tiles have a height of 56px.
|
||||||
|
*/
|
||||||
|
return 56;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const rowRenderer = ({ key, index, style }: ListRowProps): React.JSX.Element => {
|
const rowRenderer = ({ key, index, style }: ListRowProps): React.JSX.Element => {
|
||||||
if (index === memberCount) {
|
if (index === totalRows) {
|
||||||
// We've rendered all the members,
|
// We've rendered all the members,
|
||||||
// now we render an empty div to add some space to the end of the list.
|
// now we render an empty div to add some space to the end of the list.
|
||||||
return <div key={key} style={style} />;
|
return <div key={key} style={style} />;
|
||||||
@@ -37,11 +72,7 @@ const MemberListView: React.FC<IProps> = (props: IProps) => {
|
|||||||
const item = vm.members[index];
|
const item = vm.members[index];
|
||||||
return (
|
return (
|
||||||
<div key={key} style={style}>
|
<div key={key} style={style}>
|
||||||
{item.member ? (
|
{getRowComponent(item)}
|
||||||
<RoomMemberTileView member={item.member} showPresence={vm.isPresenceEnabled} />
|
|
||||||
) : (
|
|
||||||
<ThreePidInviteTileView threePidInvite={item.threePidInvite} />
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
@@ -63,11 +94,9 @@ const MemberListView: React.FC<IProps> = (props: IProps) => {
|
|||||||
{({ height, width }) => (
|
{({ height, width }) => (
|
||||||
<List
|
<List
|
||||||
rowRenderer={rowRenderer}
|
rowRenderer={rowRenderer}
|
||||||
// All the member tiles will have a height of 56px.
|
rowHeight={getRowHeight}
|
||||||
// The additional empty div at the end of the list should have a height of 32px.
|
|
||||||
rowHeight={({ index }) => (index === memberCount ? 32 : 56)}
|
|
||||||
// The +1 refers to the additional empty div that we render at the end of the list.
|
// The +1 refers to the additional empty div that we render at the end of the list.
|
||||||
rowCount={memberCount + 1}
|
rowCount={totalRows + 1}
|
||||||
// Subtract the height of MemberlistHeaderView so that the parent div does not overflow.
|
// Subtract the height of MemberlistHeaderView so that the parent div does not overflow.
|
||||||
height={height - 113}
|
height={height - 113}
|
||||||
width={width}
|
width={width}
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ import { E2EIconView } from "./common/E2EIconView";
|
|||||||
import AvatarPresenceIconView from "./common/PresenceIconView";
|
import AvatarPresenceIconView from "./common/PresenceIconView";
|
||||||
import BaseAvatar from "../../../avatars/BaseAvatar";
|
import BaseAvatar from "../../../avatars/BaseAvatar";
|
||||||
import { _t } from "../../../../../languageHandler";
|
import { _t } from "../../../../../languageHandler";
|
||||||
import { MemberTileLayout } from "./common/MemberTileLayout";
|
import { MemberTileView } from "./common/MemberTileView";
|
||||||
|
import { InvitedIconView } from "./common/InvitedIconView";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
member: RoomMember;
|
member: RoomMember;
|
||||||
@@ -43,25 +44,23 @@ export function RoomMemberTileView(props: IProps): JSX.Element {
|
|||||||
presenceJSX = <AvatarPresenceIconView presenceState={presenceState} />;
|
presenceJSX = <AvatarPresenceIconView presenceState={presenceState} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
let userLabelJSX;
|
let iconJsx;
|
||||||
if (vm.userLabel) {
|
|
||||||
userLabelJSX = <div className="mx_MemberTileView_user_label">{vm.userLabel}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
let e2eIcon;
|
|
||||||
if (vm.e2eStatus) {
|
if (vm.e2eStatus) {
|
||||||
e2eIcon = <E2EIconView status={vm.e2eStatus} />;
|
iconJsx = <E2EIconView status={vm.e2eStatus} />;
|
||||||
|
}
|
||||||
|
if (member.isInvite) {
|
||||||
|
iconJsx = <InvitedIconView isThreePid={false} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MemberTileLayout
|
<MemberTileView
|
||||||
title={vm.title}
|
title={vm.title}
|
||||||
onClick={vm.onClick}
|
onClick={vm.onClick}
|
||||||
avatarJsx={av}
|
avatarJsx={av}
|
||||||
presenceJsx={presenceJSX}
|
presenceJsx={presenceJSX}
|
||||||
nameJsx={nameJSX}
|
nameJsx={nameJSX}
|
||||||
userLabelJsx={userLabelJSX}
|
userLabel={vm.userLabel}
|
||||||
e2eIconJsx={e2eIcon}
|
iconJsx={iconJsx}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ import React from "react";
|
|||||||
import { useThreePidTileViewModel } from "../../../../viewmodels/memberlist/tiles/ThreePidTileViewModel";
|
import { useThreePidTileViewModel } from "../../../../viewmodels/memberlist/tiles/ThreePidTileViewModel";
|
||||||
import { ThreePIDInvite } from "../../../../../models/rooms/ThreePIDInvite";
|
import { ThreePIDInvite } from "../../../../../models/rooms/ThreePIDInvite";
|
||||||
import BaseAvatar from "../../../avatars/BaseAvatar";
|
import BaseAvatar from "../../../avatars/BaseAvatar";
|
||||||
import { MemberTileLayout } from "./common/MemberTileLayout";
|
import { MemberTileView } from "./common/MemberTileView";
|
||||||
|
import { InvitedIconView } from "./common/InvitedIconView";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
threePidInvite: ThreePIDInvite;
|
threePidInvite: ThreePIDInvite;
|
||||||
@@ -19,5 +20,15 @@ interface Props {
|
|||||||
export function ThreePidInviteTileView(props: Props): JSX.Element {
|
export function ThreePidInviteTileView(props: Props): JSX.Element {
|
||||||
const vm = useThreePidTileViewModel(props);
|
const vm = useThreePidTileViewModel(props);
|
||||||
const av = <BaseAvatar name={vm.name} size="32px" aria-hidden="true" />;
|
const av = <BaseAvatar name={vm.name} size="32px" aria-hidden="true" />;
|
||||||
return <MemberTileLayout nameJsx={vm.name} avatarJsx={av} onClick={vm.onClick} />;
|
const iconJsx = <InvitedIconView isThreePid={true} />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MemberTileView
|
||||||
|
nameJsx={vm.name}
|
||||||
|
avatarJsx={av}
|
||||||
|
onClick={vm.onClick}
|
||||||
|
userLabel={vm.userLabel}
|
||||||
|
iconJsx={iconJsx}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2025 New Vector Ltd.
|
||||||
|
|
||||||
|
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import EmailIcon from "@vector-im/compound-design-tokens/assets/web/icons/email-solid";
|
||||||
|
import UserAddIcon from "@vector-im/compound-design-tokens/assets/web/icons/user-add-solid";
|
||||||
|
|
||||||
|
import { Flex } from "../../../../../utils/Flex";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
isThreePid: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function InvitedIconView({ isThreePid }: Props): JSX.Element {
|
||||||
|
const Icon = isThreePid ? EmailIcon : UserAddIcon;
|
||||||
|
return (
|
||||||
|
<Flex align="center" className="mx_InvitedIconView">
|
||||||
|
<Icon height="16px" width="16px" />
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
}
|
||||||
+10
-5
@@ -15,11 +15,16 @@ interface Props {
|
|||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
title?: string;
|
title?: string;
|
||||||
presenceJsx?: JSX.Element;
|
presenceJsx?: JSX.Element;
|
||||||
userLabelJsx?: JSX.Element;
|
userLabel?: React.ReactNode;
|
||||||
e2eIconJsx?: JSX.Element;
|
iconJsx?: JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function MemberTileLayout(props: Props): JSX.Element {
|
export function MemberTileView(props: Props): JSX.Element {
|
||||||
|
let userLabelJsx: React.ReactNode;
|
||||||
|
if (props.userLabel) {
|
||||||
|
userLabelJsx = <div className="mx_MemberTileView_userLabel">{props.userLabel}</div>;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
// The wrapping div is required to make the magic mouse listener work, for some reason.
|
// The wrapping div is required to make the magic mouse listener work, for some reason.
|
||||||
<div>
|
<div>
|
||||||
@@ -31,8 +36,8 @@ export function MemberTileLayout(props: Props): JSX.Element {
|
|||||||
<div className="mx_MemberTileView_name">{props.nameJsx}</div>
|
<div className="mx_MemberTileView_name">{props.nameJsx}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mx_MemberTileView_right">
|
<div className="mx_MemberTileView_right">
|
||||||
{props.userLabelJsx}
|
{userLabelJsx}
|
||||||
{props.e2eIconJsx}
|
{props.iconJsx}
|
||||||
</div>
|
</div>
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
</div>
|
</div>
|
||||||
+23
-1
@@ -224,7 +224,29 @@ exports[`MemberTileView ThreePidInviteTileView renders ThreePidInvite correctly
|
|||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="mx_MemberTileView_right"
|
class="mx_MemberTileView_right"
|
||||||
/>
|
>
|
||||||
|
<div
|
||||||
|
class="mx_MemberTileView_userLabel"
|
||||||
|
>
|
||||||
|
(Invited)
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="mx_Flex mx_InvitedIconView"
|
||||||
|
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: start; --mx-flex-gap: 0;"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
fill="currentColor"
|
||||||
|
height="16px"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
width="16px"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M4 4h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2Zm0 5.111a1 1 0 0 0 .514.874l7 3.89a1 1 0 0 0 .972 0l7-3.89a1 1 0 1 0-.972-1.748L12 11.856 5.486 8.237A1 1 0 0 0 4 9.111Z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user