From 6eb51b34bf152b88eca6bc94dc1c9c5ae63e5a77 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Sun, 5 Jan 2025 20:36:03 +0530 Subject: [PATCH] Implement new memberlist header view --- .../views/rooms/_MemberListHeaderView.pcss | 37 +++++ .../rooms/MemberList/MemberListHeaderView.tsx | 137 ++++++++++++++++++ 2 files changed, 174 insertions(+) create mode 100644 res/css/views/rooms/_MemberListHeaderView.pcss create mode 100644 src/components/views/rooms/MemberList/MemberListHeaderView.tsx diff --git a/res/css/views/rooms/_MemberListHeaderView.pcss b/res/css/views/rooms/_MemberListHeaderView.pcss new file mode 100644 index 0000000000..0143dda501 --- /dev/null +++ b/res/css/views/rooms/_MemberListHeaderView.pcss @@ -0,0 +1,37 @@ +/* +Copyright 2024 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only +Please see LICENSE files in the repository root for full details. +*/ + +.mx_MemberListHeaderView { + border-bottom: var(--cpd-border-width-1) solid var(--cpd-color-gray-400); + max-height: 112px; + + .mx_MemberListHeaderView_container { + margin-top: var(--cpd-space-6x); + width: 100%; + } + + .mx_MemberListHeaderView_invite_small { + margin-left: var(--cpd-space-3x); + } + + .mx_MemberListHeaderView_invite_large { + width: 288px; + height: 36px; + } + + .mx_MemberListHeaderView_label { + padding: var(--cpd-space-6x) 0 var(--cpd-space-2x) var(--cpd-space-4x); + box-sizing: border-box; + width: 100%; + color: var(--cpd-color-text-secondary); + font: var(--cpd-font-body-sm-semibold); + } + + .mx_MemberListHeaderView_search { + width: 240px; + } +} diff --git a/src/components/views/rooms/MemberList/MemberListHeaderView.tsx b/src/components/views/rooms/MemberList/MemberListHeaderView.tsx new file mode 100644 index 0000000000..93ce1ca665 --- /dev/null +++ b/src/components/views/rooms/MemberList/MemberListHeaderView.tsx @@ -0,0 +1,137 @@ +/* +Copyright 2024 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only +Please see LICENSE files in the repository root for full details. +*/ + +import { Search, Text, Button, Tooltip, InlineSpinner } from "@vector-im/compound-web"; +import React from "react"; +import InviteIcon from "@vector-im/compound-design-tokens/assets/web/icons/user-add-solid"; +import { UserAddIcon } from "@vector-im/compound-design-tokens/assets/web/icons"; + +import { Flex } from "../../../utils/Flex"; +import { MemberListViewState } from "../../../viewmodels/memberlist/MemberListViewModel"; +import { _t } from "../../../../languageHandler"; + +interface TooltipProps { + canInvite: boolean; + children: React.ReactNode; +} + +const OptionalTooltip: React.FC = ({ canInvite, children }) => { + if (canInvite) return children; + // If the user isn't allowed to invite others to this room, wrap with a relevant tooltip. + return {children}; +}; + +interface Props { + vm: MemberListViewState; +} + +const InviteButton: React.FC = ({ vm }) => { + const shouldShowInvite = vm.shouldShowInvite; + const shouldShowSearch = vm.shouldShowSearch; + const disabled = !vm.canInvite; + + if (!shouldShowInvite) { + // In this case, invite button should not be rendered. + return null; + } + + if (shouldShowSearch) { + /// When rendered alongside a search box, the invite button is just an icon. + return ( + + + + ); +}; + +/** + * This should be: + * A loading text with spinner while the memberlist loads. + * Member count of the room when there's nothing in the search field. + * Number of matching members during search or 'No result' if search found nothing. + */ +function getHeaderLabelJSX(vm: MemberListViewState): React.ReactNode { + if (vm.isLoading) { + return ( + + {_t("common|loading")} + + ); + } + + const filteredMemberCount = vm.members.length; + if (filteredMemberCount === 0) { + return _t("member_list|no_matches"); + } + return _t("member_list|count", { count: filteredMemberCount }); +} + +export const MemberListHeaderView: React.FC = (props: Props) => { + const vm = props.vm; + + let contentJSX: React.ReactNode; + + if (vm.shouldShowSearch) { + // When we need to show the search box + contentJSX = ( + + vm.search((e as React.ChangeEvent).target.value)} + /> + + + ); + } else if (!vm.shouldShowSearch && vm.shouldShowInvite) { + // When we don't need to show the search box but still need an invite button + contentJSX = ( + + + + ); + } else { + // No search box and no invite icon, so nothing to render! + contentJSX = null; + } + + return ( + + {!vm.isLoading && contentJSX} + + {getHeaderLabelJSX(vm)} + + + ); +};