From a2e53f9c499b6c351737cf8dc769234fb5b8b1b6 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Mon, 13 Jan 2025 00:19:17 +0530 Subject: [PATCH 01/11] Fix tiny typo in existing code --- src/stores/right-panel/RightPanelStore.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/right-panel/RightPanelStore.ts b/src/stores/right-panel/RightPanelStore.ts index ea9b722071d..42077daded9 100644 --- a/src/stores/right-panel/RightPanelStore.ts +++ b/src/stores/right-panel/RightPanelStore.ts @@ -239,7 +239,7 @@ export default class RightPanelStore extends ReadyWatchingStore { * @param cardState The state within the phase. */ public showOrHidePhase(phase: RightPanelPhases, cardState?: Partial): void { - if (this.currentCard.phase == phase && !cardState && this.isOpen) { + if (this.currentCard.phase === phase && !cardState && this.isOpen) { this.togglePanel(null); } else { this.setCard({ phase, state: cardState }); From 921ec06bd3482a021d1644a8ea049f4cc529252e Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Mon, 13 Jan 2025 00:21:55 +0530 Subject: [PATCH 02/11] Create a hook that uses the right panel store So that we track changes to the right panel phases --- src/hooks/right-panel/useCurrentPhase.ts | 45 ++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/hooks/right-panel/useCurrentPhase.ts diff --git a/src/hooks/right-panel/useCurrentPhase.ts b/src/hooks/right-panel/useCurrentPhase.ts new file mode 100644 index 00000000000..48746f97147 --- /dev/null +++ b/src/hooks/right-panel/useCurrentPhase.ts @@ -0,0 +1,45 @@ +/* +Copyright 2024 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 { useContext, useState } from "react"; + +import { SDKContext } from "../../contexts/SDKContext"; +import { RightPanelPhases } from "../../stores/right-panel/RightPanelStorePhases"; +import { useEventEmitter } from "../useEventEmitter"; +import { UPDATE_EVENT } from "../../stores/AsyncStore"; + +/** + * Returns: + * - state which will always reflect the currently active right panel phase or null. + * - boolean state representing whether any panel is open or not. + * @param roomId room id if available. + */ +export function useCurrentPhase(roomId?: string): { currentPhase: RightPanelPhases | null; isOpen: boolean } { + const sdkContext = useContext(SDKContext); + + const getCurrentPhase = (): RightPanelPhases | null => { + const card = roomId + ? sdkContext.rightPanelStore.currentCardForRoom(roomId) + : sdkContext.rightPanelStore.currentCard; + return card.phase; + }; + + const getIsOpen = (): boolean => { + const isOpen = roomId ? sdkContext.rightPanelStore.isOpenForRoom(roomId) : sdkContext.rightPanelStore.isOpen; + return isOpen; + }; + + const [currentPhase, setCurrentPhase] = useState(getCurrentPhase()); + const [isOpen, setIsOpen] = useState(getIsOpen()); + + useEventEmitter(sdkContext.rightPanelStore, UPDATE_EVENT, () => { + setCurrentPhase(getCurrentPhase()); + setIsOpen(getIsOpen()); + }); + + return { currentPhase, isOpen }; +} From 0a5f7379db9637f01f8c60c0cffa73af6c1cd578 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Mon, 13 Jan 2025 00:23:17 +0530 Subject: [PATCH 03/11] Create a context that wraps the previous hook we created We do this so that we can get by using a single event listener i.e we only need to call `useCurrentPhase` in the provider as opposed to calling it in each header icon. --- .../CurrentRightPanelPhaseContext.tsx | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/contexts/CurrentRightPanelPhaseContext.tsx diff --git a/src/contexts/CurrentRightPanelPhaseContext.tsx b/src/contexts/CurrentRightPanelPhaseContext.tsx new file mode 100644 index 00000000000..5526f59a748 --- /dev/null +++ b/src/contexts/CurrentRightPanelPhaseContext.tsx @@ -0,0 +1,34 @@ +/* +Copyright 2024 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, { createContext } from "react"; + +import { useCurrentPhase } from "../hooks/right-panel/useCurrentPhase"; +import { RightPanelPhases } from "../stores/right-panel/RightPanelStorePhases"; + +type Context = { + isPanelOpen: boolean; + currentPhase: RightPanelPhases | null; +}; + +export const CurrentRightPanelPhaseContext = createContext(null); + +type Props = { + roomId: string; +}; + +export const CurrentRightPanelPhaseContextProvider: React.FC> = ({ + roomId, + children, +}) => { + const { currentPhase, isOpen } = useCurrentPhase(roomId); + return ( + + {children} + + ); +}; From 402cf5680ceab253f9b3af4001a1c65aca988985 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Mon, 13 Jan 2025 00:28:07 +0530 Subject: [PATCH 04/11] Create a hook that tells you if a panel is open or not --- .../rooms/RoomHeader/highlight/useToggled.tsx | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/components/views/rooms/RoomHeader/highlight/useToggled.tsx diff --git a/src/components/views/rooms/RoomHeader/highlight/useToggled.tsx b/src/components/views/rooms/RoomHeader/highlight/useToggled.tsx new file mode 100644 index 00000000000..28f36fe0f98 --- /dev/null +++ b/src/components/views/rooms/RoomHeader/highlight/useToggled.tsx @@ -0,0 +1,23 @@ +/* +Copyright 2024 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 { useContext } from "react"; + +import { RightPanelPhases } from "../../../../../stores/right-panel/RightPanelStorePhases"; +import { CurrentRightPanelPhaseContext } from "../../../../../contexts/CurrentRightPanelPhaseContext"; + +/** + * Hook to easily track whether a given right panel phase is toggled on/off. + */ +export function useToggled(phase: RightPanelPhases): boolean { + const context = useContext(CurrentRightPanelPhaseContext); + if (!context) { + throw new Error("Context is null, did you forget to use CurrentRightPanelPhaseContextProvider?"); + } + const { currentPhase, isPanelOpen } = context; + return !!(isPanelOpen && currentPhase === phase); +} From 13b26bbab59057d1b91c8fb54dd93096c72eca49 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Mon, 13 Jan 2025 00:32:32 +0530 Subject: [PATCH 05/11] Create component that wraps Icon and adds a class name when the corresponding panel is open --- .../RoomHeader/highlight/ToggleableIcon.tsx | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/components/views/rooms/RoomHeader/highlight/ToggleableIcon.tsx diff --git a/src/components/views/rooms/RoomHeader/highlight/ToggleableIcon.tsx b/src/components/views/rooms/RoomHeader/highlight/ToggleableIcon.tsx new file mode 100644 index 00000000000..4a5f1a73959 --- /dev/null +++ b/src/components/views/rooms/RoomHeader/highlight/ToggleableIcon.tsx @@ -0,0 +1,30 @@ +/* +Copyright 2024 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 classNames from "classnames"; + +import { RightPanelPhases } from "../../../../../stores/right-panel/RightPanelStorePhases"; +import { useToggled } from "./useToggled"; + +type Props = { + Icon: React.ComponentType; + phase: RightPanelPhases; +}; + +/** + * Use this component for room header icons that toggle different right panel phases. + * Will add a class to the icon when the specified phase is on. + */ +export function ToggleableIcon({ Icon, phase }: Props): React.ReactElement { + const toggled = useToggled(phase); + const highlightClass = classNames({ + mx_RoomHeader_toggled: toggled, + }); + + return ; +} From 14ae32156b7057359df1f754eb6250dbd21f9e37 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Mon, 13 Jan 2025 01:02:19 +0530 Subject: [PATCH 06/11] Style room header icons for when they are toggled --- res/css/views/rooms/_RoomHeader.pcss | 4 + src/components/structures/RoomView.tsx | 2 +- .../WaitingForThirdPartyRoomView.tsx | 2 +- src/components/views/rooms/RoomHeader.tsx | 408 ----------------- .../views/rooms/RoomHeader/RoomHeader.tsx | 412 ++++++++++++++++++ .../rooms/RoomHeader/VideoRoomChatButton.tsx | 3 +- .../{ => RoomHeader}/RoomHeader-test.tsx | 40 +- 7 files changed, 440 insertions(+), 431 deletions(-) delete mode 100644 src/components/views/rooms/RoomHeader.tsx create mode 100644 src/components/views/rooms/RoomHeader/RoomHeader.tsx rename test/unit-tests/components/views/rooms/{ => RoomHeader}/RoomHeader-test.tsx (95%) diff --git a/res/css/views/rooms/_RoomHeader.pcss b/res/css/views/rooms/_RoomHeader.pcss index 32eb055f07e..66b6bdde307 100644 --- a/res/css/views/rooms/_RoomHeader.pcss +++ b/res/css/views/rooms/_RoomHeader.pcss @@ -93,3 +93,7 @@ Please see LICENSE files in the repository root for full details. /* Workaround for https://github.com/element-hq/compound/issues/331 */ min-width: 240px; } + +.mx_RoomHeader .mx_RoomHeader_toggled { + color: var(--cpd-color-icon-accent-primary); +} diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 78d14d39dfb..c9b465a11c4 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -66,7 +66,7 @@ import RoomPreviewBar from "../views/rooms/RoomPreviewBar"; import RoomPreviewCard from "../views/rooms/RoomPreviewCard"; import RoomUpgradeWarningBar from "../views/rooms/RoomUpgradeWarningBar"; import AuxPanel from "../views/rooms/AuxPanel"; -import RoomHeader from "../views/rooms/RoomHeader"; +import RoomHeader from "../views/rooms/RoomHeader/RoomHeader"; import { IOOBData, IThreepidInvite } from "../../stores/ThreepidInviteStore"; import EffectsOverlay from "../views/elements/EffectsOverlay"; import { containsEmoji } from "../../effects/utils"; diff --git a/src/components/structures/WaitingForThirdPartyRoomView.tsx b/src/components/structures/WaitingForThirdPartyRoomView.tsx index 7787a03bf26..065ed39cdb1 100644 --- a/src/components/structures/WaitingForThirdPartyRoomView.tsx +++ b/src/components/structures/WaitingForThirdPartyRoomView.tsx @@ -11,7 +11,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/matrix"; import ResizeNotifier from "../../utils/ResizeNotifier"; import ErrorBoundary from "../views/elements/ErrorBoundary"; -import RoomHeader from "../views/rooms/RoomHeader"; +import RoomHeader from "../views/rooms/RoomHeader/RoomHeader.tsx"; import ScrollPanel from "./ScrollPanel"; import EventTileBubble from "../views/messages/EventTileBubble"; import NewRoomIntro from "../views/rooms/NewRoomIntro"; diff --git a/src/components/views/rooms/RoomHeader.tsx b/src/components/views/rooms/RoomHeader.tsx deleted file mode 100644 index afe68ee5bec..00000000000 --- a/src/components/views/rooms/RoomHeader.tsx +++ /dev/null @@ -1,408 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2023 The Matrix.org Foundation C.I.C. - -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, { useCallback, useMemo, useState } from "react"; -import { Body as BodyText, Button, IconButton, Menu, MenuItem, Tooltip } from "@vector-im/compound-web"; -import VideoCallIcon from "@vector-im/compound-design-tokens/assets/web/icons/video-call-solid"; -import VoiceCallIcon from "@vector-im/compound-design-tokens/assets/web/icons/voice-call"; -import CloseCallIcon from "@vector-im/compound-design-tokens/assets/web/icons/close"; -import ThreadsIcon from "@vector-im/compound-design-tokens/assets/web/icons/threads-solid"; -import RoomInfoIcon from "@vector-im/compound-design-tokens/assets/web/icons/info-solid"; -import NotificationsIcon from "@vector-im/compound-design-tokens/assets/web/icons/notifications-solid"; -import VerifiedIcon from "@vector-im/compound-design-tokens/assets/web/icons/verified"; -import ErrorIcon from "@vector-im/compound-design-tokens/assets/web/icons/error"; -import PublicIcon from "@vector-im/compound-design-tokens/assets/web/icons/public"; -import { JoinRule, type Room } from "matrix-js-sdk/src/matrix"; -import { ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle"; - -import { useRoomName } from "../../../hooks/useRoomName"; -import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases"; -import { useMatrixClientContext } from "../../../contexts/MatrixClientContext"; -import { useRoomMemberCount, useRoomMembers } from "../../../hooks/useRoomMembers"; -import { _t } from "../../../languageHandler"; -import { Flex } from "../../utils/Flex"; -import { Box } from "../../utils/Box"; -import { getPlatformCallTypeProps, useRoomCall } from "../../../hooks/room/useRoomCall"; -import { useRoomThreadNotifications } from "../../../hooks/room/useRoomThreadNotifications"; -import { useGlobalNotificationState } from "../../../hooks/useGlobalNotificationState"; -import SdkConfig from "../../../SdkConfig"; -import { useFeatureEnabled } from "../../../hooks/useSettings"; -import { useEncryptionStatus } from "../../../hooks/useEncryptionStatus"; -import { E2EStatus } from "../../../utils/ShieldUtils"; -import FacePile from "../elements/FacePile"; -import { useRoomState } from "../../../hooks/useRoomState"; -import RoomAvatar from "../avatars/RoomAvatar"; -import { formatCount } from "../../../utils/FormattingUtils"; -import RightPanelStore from "../../../stores/right-panel/RightPanelStore"; -import PosthogTrackers from "../../../PosthogTrackers"; -import { VideoRoomChatButton } from "./RoomHeader/VideoRoomChatButton"; -import { RoomKnocksBar } from "./RoomKnocksBar"; -import { isVideoRoom as calcIsVideoRoom } from "../../../utils/video-rooms"; -import { notificationLevelToIndicator } from "../../../utils/notifications"; -import { CallGuestLinkButton } from "./RoomHeader/CallGuestLinkButton"; -import { ButtonEvent } from "../elements/AccessibleButton"; -import WithPresenceIndicator, { useDmMember } from "../avatars/WithPresenceIndicator"; -import { IOOBData } from "../../../stores/ThreepidInviteStore"; -import { MainSplitContentType } from "../../structures/RoomView"; -import defaultDispatcher from "../../../dispatcher/dispatcher.ts"; -import { RoomSettingsTab } from "../dialogs/RoomSettingsDialog.tsx"; -import { useScopedRoomContext } from "../../../contexts/ScopedRoomContext.tsx"; - -export default function RoomHeader({ - room, - additionalButtons, - oobData, -}: { - room: Room; - additionalButtons?: ViewRoomOpts["buttons"]; - oobData?: IOOBData; -}): JSX.Element { - const client = useMatrixClientContext(); - - const roomName = useRoomName(room); - const joinRule = useRoomState(room, (state) => state.getJoinRule()); - - const members = useRoomMembers(room, 2500); - const memberCount = useRoomMemberCount(room, { throttleWait: 2500 }); - - const { - voiceCallDisabledReason, - voiceCallClick, - videoCallDisabledReason, - videoCallClick, - toggleCallMaximized: toggleCall, - isViewingCall, - isConnectedToCall, - hasActiveCallSession, - callOptions, - showVoiceCallButton, - showVideoCallButton, - } = useRoomCall(room); - - const groupCallsEnabled = useFeatureEnabled("feature_group_calls"); - /** - * A special mode where only Element Call is used. In this case we want to - * hide the voice call button - */ - const useElementCallExclusively = useMemo(() => { - return SdkConfig.get("element_call").use_exclusively && groupCallsEnabled; - }, [groupCallsEnabled]); - - const threadNotifications = useRoomThreadNotifications(room); - const globalNotificationState = useGlobalNotificationState(); - - const dmMember = useDmMember(room); - const isDirectMessage = !!dmMember; - const e2eStatus = useEncryptionStatus(client, room); - - const notificationsEnabled = useFeatureEnabled("feature_notifications"); - - const askToJoinEnabled = useFeatureEnabled("feature_ask_to_join"); - - const videoClick = useCallback( - (ev: React.MouseEvent) => videoCallClick(ev, callOptions[0]), - [callOptions, videoCallClick], - ); - - const toggleCallButton = ( - - - - - - ); - - const joinCallButton = ( - - - - ); - - const callIconWithTooltip = ( - - - - ); - - const [menuOpen, setMenuOpen] = useState(false); - - const onOpenChange = useCallback( - (newOpen: boolean) => { - if (!videoCallDisabledReason) setMenuOpen(newOpen); - }, - [videoCallDisabledReason], - ); - - const startVideoCallButton = ( - <> - {/* Can be either a menu or just a button depending on the number of call options.*/} - {callOptions.length > 1 ? ( - - {callIconWithTooltip} - - } - side="left" - align="start" - > - {callOptions.map((option) => { - const { label, children } = getPlatformCallTypeProps(option); - return ( - videoCallClick(ev, option)} - Icon={VideoCallIcon} - onSelect={() => {} /* Dummy handler since we want the click event.*/} - /> - ); - })} - - ) : ( - - {callIconWithTooltip} - - )} - - ); - let voiceCallButton: JSX.Element | undefined = ( - - voiceCallClick(ev, callOptions[0])} - > - - - - ); - const closeLobbyButton = ( - - - - - - ); - let videoCallButton: JSX.Element | undefined = startVideoCallButton; - if (isConnectedToCall) { - videoCallButton = toggleCallButton; - } else if (isViewingCall) { - videoCallButton = closeLobbyButton; - } - - if (!showVideoCallButton) { - videoCallButton = undefined; - } - if (!showVoiceCallButton) { - voiceCallButton = undefined; - } - - const roomContext = useScopedRoomContext("mainSplitContentType"); - const isVideoRoom = calcIsVideoRoom(room); - const showChatButton = - isVideoRoom || - roomContext.mainSplitContentType === MainSplitContentType.MaximisedWidget || - roomContext.mainSplitContentType === MainSplitContentType.Call; - - const onAvatarClick = (): void => { - defaultDispatcher.dispatch({ - action: "open_room_settings", - initial_tab_id: RoomSettingsTab.General, - }); - }; - - return ( - <> - - - {/* We hide this from the tabIndex list as it is a pointer shortcut and superfluous for a11y */} - - - - - {additionalButtons?.map((props) => { - const label = props.label(); - - return ( - - { - event.stopPropagation(); - props.onClick(); - }} - > - {typeof props.icon === "function" ? props.icon() : props.icon} - - - ); - })} - - {isViewingCall && } - - {hasActiveCallSession && !isConnectedToCall && !isViewingCall ? ( - joinCallButton - ) : ( - <> - {!isVideoRoom && videoCallButton} - {!useElementCallExclusively && !isVideoRoom && voiceCallButton} - - )} - - {showChatButton && } - - - { - evt.stopPropagation(); - RightPanelStore.instance.showOrHidePhase(RightPanelPhases.ThreadPanel); - PosthogTrackers.trackInteraction("WebRoomHeaderButtonsThreadsButton", evt); - }} - aria-label={_t("common|threads")} - > - - - - {notificationsEnabled && ( - - { - evt.stopPropagation(); - RightPanelStore.instance.showOrHidePhase(RightPanelPhases.NotificationPanel); - }} - aria-label={_t("notifications|enable_prompt_toast_title")} - > - - - - )} - - - { - evt.stopPropagation(); - RightPanelStore.instance.showOrHidePhase(RightPanelPhases.RoomSummary); - }} - aria-label={_t("right_panel|room_summary_card|title")} - > - - - - - {!isDirectMessage && ( - - { - RightPanelStore.instance.showOrHidePhase(RightPanelPhases.MemberList); - e.stopPropagation(); - }} - aria-label={_t("common|n_members", { count: memberCount })} - > - {formatCount(memberCount)} - - - )} - - {askToJoinEnabled && } - - ); -} diff --git a/src/components/views/rooms/RoomHeader/RoomHeader.tsx b/src/components/views/rooms/RoomHeader/RoomHeader.tsx new file mode 100644 index 00000000000..5f8bf22d17f --- /dev/null +++ b/src/components/views/rooms/RoomHeader/RoomHeader.tsx @@ -0,0 +1,412 @@ +/* +Copyright 2024 New Vector Ltd. +Copyright 2023 The Matrix.org Foundation C.I.C. + +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, { useCallback, useMemo, useState } from "react"; +import { Body as BodyText, Button, IconButton, Menu, MenuItem, Tooltip } from "@vector-im/compound-web"; +import VideoCallIcon from "@vector-im/compound-design-tokens/assets/web/icons/video-call-solid"; +import VoiceCallIcon from "@vector-im/compound-design-tokens/assets/web/icons/voice-call"; +import CloseCallIcon from "@vector-im/compound-design-tokens/assets/web/icons/close"; +import ThreadsIcon from "@vector-im/compound-design-tokens/assets/web/icons/threads-solid"; +import RoomInfoIcon from "@vector-im/compound-design-tokens/assets/web/icons/info-solid"; +import NotificationsIcon from "@vector-im/compound-design-tokens/assets/web/icons/notifications-solid"; +import VerifiedIcon from "@vector-im/compound-design-tokens/assets/web/icons/verified"; +import ErrorIcon from "@vector-im/compound-design-tokens/assets/web/icons/error"; +import PublicIcon from "@vector-im/compound-design-tokens/assets/web/icons/public"; +import { JoinRule, type Room } from "matrix-js-sdk/src/matrix"; +import { ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle"; + +import { useRoomName } from "../../../../hooks/useRoomName.ts"; +import { RightPanelPhases } from "../../../../stores/right-panel/RightPanelStorePhases.ts"; +import { useMatrixClientContext } from "../../../../contexts/MatrixClientContext.tsx"; +import { useRoomMemberCount, useRoomMembers } from "../../../../hooks/useRoomMembers.ts"; +import { _t } from "../../../../languageHandler.tsx"; +import { Flex } from "../../../utils/Flex.tsx"; +import { Box } from "../../../utils/Box.tsx"; +import { getPlatformCallTypeProps, useRoomCall } from "../../../../hooks/room/useRoomCall.tsx"; +import { useRoomThreadNotifications } from "../../../../hooks/room/useRoomThreadNotifications.ts"; +import { useGlobalNotificationState } from "../../../../hooks/useGlobalNotificationState.ts"; +import SdkConfig from "../../../../SdkConfig.ts"; +import { useFeatureEnabled } from "../../../../hooks/useSettings.ts"; +import { useEncryptionStatus } from "../../../../hooks/useEncryptionStatus.ts"; +import { E2EStatus } from "../../../../utils/ShieldUtils.ts"; +import FacePile from "../../elements/FacePile.tsx"; +import { useRoomState } from "../../../../hooks/useRoomState.ts"; +import RoomAvatar from "../../avatars/RoomAvatar.tsx"; +import { formatCount } from "../../../../utils/FormattingUtils.ts"; +import RightPanelStore from "../../../../stores/right-panel/RightPanelStore.ts"; +import PosthogTrackers from "../../../../PosthogTrackers.ts"; +import { VideoRoomChatButton } from "./VideoRoomChatButton.tsx"; +import { RoomKnocksBar } from "../RoomKnocksBar.tsx"; +import { isVideoRoom as calcIsVideoRoom } from "../../../../utils/video-rooms.ts"; +import { notificationLevelToIndicator } from "../../../../utils/notifications.ts"; +import { CallGuestLinkButton } from "./CallGuestLinkButton.tsx"; +import { ButtonEvent } from "../../elements/AccessibleButton.tsx"; +import WithPresenceIndicator, { useDmMember } from "../../avatars/WithPresenceIndicator.tsx"; +import { IOOBData } from "../../../../stores/ThreepidInviteStore.ts"; +import { MainSplitContentType } from "../../../structures/RoomView.tsx"; +import defaultDispatcher from "../../../../dispatcher/dispatcher.ts"; +import { RoomSettingsTab } from "../../dialogs/RoomSettingsDialog.tsx"; +import { useScopedRoomContext } from "../../../../contexts/ScopedRoomContext.tsx"; +import { ToggleableIcon } from "./highlight/ToggleableIcon.tsx"; +import { CurrentRightPanelPhaseContextProvider } from "../../../../contexts/CurrentRightPanelPhaseContext.tsx"; + +export default function RoomHeader({ + room, + additionalButtons, + oobData, +}: { + room: Room; + additionalButtons?: ViewRoomOpts["buttons"]; + oobData?: IOOBData; +}): JSX.Element { + const client = useMatrixClientContext(); + + const roomName = useRoomName(room); + const joinRule = useRoomState(room, (state) => state.getJoinRule()); + + const members = useRoomMembers(room, 2500); + const memberCount = useRoomMemberCount(room, { throttleWait: 2500 }); + + const { + voiceCallDisabledReason, + voiceCallClick, + videoCallDisabledReason, + videoCallClick, + toggleCallMaximized: toggleCall, + isViewingCall, + isConnectedToCall, + hasActiveCallSession, + callOptions, + showVoiceCallButton, + showVideoCallButton, + } = useRoomCall(room); + + const groupCallsEnabled = useFeatureEnabled("feature_group_calls"); + /** + * A special mode where only Element Call is used. In this case we want to + * hide the voice call button + */ + const useElementCallExclusively = useMemo(() => { + return SdkConfig.get("element_call").use_exclusively && groupCallsEnabled; + }, [groupCallsEnabled]); + + const threadNotifications = useRoomThreadNotifications(room); + const globalNotificationState = useGlobalNotificationState(); + + const dmMember = useDmMember(room); + const isDirectMessage = !!dmMember; + const e2eStatus = useEncryptionStatus(client, room); + + const notificationsEnabled = useFeatureEnabled("feature_notifications"); + + const askToJoinEnabled = useFeatureEnabled("feature_ask_to_join"); + + const videoClick = useCallback( + (ev: React.MouseEvent) => videoCallClick(ev, callOptions[0]), + [callOptions, videoCallClick], + ); + + const toggleCallButton = ( + + + + + + ); + + const joinCallButton = ( + + + + ); + + const callIconWithTooltip = ( + + + + ); + + const [menuOpen, setMenuOpen] = useState(false); + + const onOpenChange = useCallback( + (newOpen: boolean) => { + if (!videoCallDisabledReason) setMenuOpen(newOpen); + }, + [videoCallDisabledReason], + ); + + const startVideoCallButton = ( + <> + {/* Can be either a menu or just a button depending on the number of call options.*/} + {callOptions.length > 1 ? ( + + {callIconWithTooltip} + + } + side="left" + align="start" + > + {callOptions.map((option) => { + const { label, children } = getPlatformCallTypeProps(option); + return ( + videoCallClick(ev, option)} + Icon={VideoCallIcon} + onSelect={() => {} /* Dummy handler since we want the click event.*/} + /> + ); + })} + + ) : ( + + {callIconWithTooltip} + + )} + + ); + let voiceCallButton: JSX.Element | undefined = ( + + voiceCallClick(ev, callOptions[0])} + > + + + + ); + const closeLobbyButton = ( + + + + + + ); + let videoCallButton: JSX.Element | undefined = startVideoCallButton; + if (isConnectedToCall) { + videoCallButton = toggleCallButton; + } else if (isViewingCall) { + videoCallButton = closeLobbyButton; + } + + if (!showVideoCallButton) { + videoCallButton = undefined; + } + if (!showVoiceCallButton) { + voiceCallButton = undefined; + } + + const roomContext = useScopedRoomContext("mainSplitContentType"); + const isVideoRoom = calcIsVideoRoom(room); + const showChatButton = + isVideoRoom || + roomContext.mainSplitContentType === MainSplitContentType.MaximisedWidget || + roomContext.mainSplitContentType === MainSplitContentType.Call; + + const onAvatarClick = (): void => { + defaultDispatcher.dispatch({ + action: "open_room_settings", + initial_tab_id: RoomSettingsTab.General, + }); + }; + + return ( + <> + + + + {/* We hide this from the tabIndex list as it is a pointer shortcut and superfluous for a11y */} + + + + + {additionalButtons?.map((props) => { + const label = props.label(); + + return ( + + { + event.stopPropagation(); + props.onClick(); + }} + > + {typeof props.icon === "function" ? props.icon() : props.icon} + + + ); + })} + + {isViewingCall && } + + {hasActiveCallSession && !isConnectedToCall && !isViewingCall ? ( + joinCallButton + ) : ( + <> + {!isVideoRoom && videoCallButton} + {!useElementCallExclusively && !isVideoRoom && voiceCallButton} + + )} + + {showChatButton && } + + + { + evt.stopPropagation(); + RightPanelStore.instance.showOrHidePhase(RightPanelPhases.ThreadPanel); + PosthogTrackers.trackInteraction("WebRoomHeaderButtonsThreadsButton", evt); + }} + aria-label={_t("common|threads")} + > + + + + {notificationsEnabled && ( + + { + evt.stopPropagation(); + RightPanelStore.instance.showOrHidePhase(RightPanelPhases.NotificationPanel); + }} + aria-label={_t("notifications|enable_prompt_toast_title")} + > + + + + )} + + + { + evt.stopPropagation(); + RightPanelStore.instance.showOrHidePhase(RightPanelPhases.RoomSummary); + }} + aria-label={_t("right_panel|room_summary_card|title")} + > + + + + + {!isDirectMessage && ( + + { + RightPanelStore.instance.showOrHidePhase(RightPanelPhases.MemberList); + e.stopPropagation(); + }} + aria-label={_t("common|n_members", { count: memberCount })} + > + {formatCount(memberCount)} + + + )} + + {askToJoinEnabled && } + + + ); +} diff --git a/src/components/views/rooms/RoomHeader/VideoRoomChatButton.tsx b/src/components/views/rooms/RoomHeader/VideoRoomChatButton.tsx index 0dbf62dfda3..72061b52495 100644 --- a/src/components/views/rooms/RoomHeader/VideoRoomChatButton.tsx +++ b/src/components/views/rooms/RoomHeader/VideoRoomChatButton.tsx @@ -18,6 +18,7 @@ import { NotificationLevel } from "../../../../stores/notifications/Notification import { RightPanelPhases } from "../../../../stores/right-panel/RightPanelStorePhases"; import { SDKContext } from "../../../../contexts/SDKContext"; import { ButtonEvent } from "../../elements/AccessibleButton"; +import { ToggleableIcon } from "./highlight/ToggleableIcon"; /** * Display a button to toggle timeline for video rooms @@ -54,7 +55,7 @@ export const VideoRoomChatButton: React.FC<{ room: Room }> = ({ room }) => { onClick={onClick} indicator={displayUnreadIndicator ? "default" : undefined} > - + ); diff --git a/test/unit-tests/components/views/rooms/RoomHeader-test.tsx b/test/unit-tests/components/views/rooms/RoomHeader/RoomHeader-test.tsx similarity index 95% rename from test/unit-tests/components/views/rooms/RoomHeader-test.tsx rename to test/unit-tests/components/views/rooms/RoomHeader/RoomHeader-test.tsx index 633100eec4c..481f8501cda 100644 --- a/test/unit-tests/components/views/rooms/RoomHeader-test.tsx +++ b/test/unit-tests/components/views/rooms/RoomHeader/RoomHeader-test.tsx @@ -36,26 +36,26 @@ import { import { ViewRoomOpts } from "@matrix-org/react-sdk-module-api/lib/lifecycles/RoomViewLifecycle"; import { mocked } from "jest-mock"; -import { filterConsole, stubClient } from "../../../../test-utils"; -import RoomHeader from "../../../../../src/components/views/rooms/RoomHeader"; -import DMRoomMap from "../../../../../src/utils/DMRoomMap"; -import { MatrixClientPeg } from "../../../../../src/MatrixClientPeg"; -import RightPanelStore from "../../../../../src/stores/right-panel/RightPanelStore"; -import { RightPanelPhases } from "../../../../../src/stores/right-panel/RightPanelStorePhases"; -import LegacyCallHandler from "../../../../../src/LegacyCallHandler"; -import SettingsStore from "../../../../../src/settings/SettingsStore"; -import SdkConfig from "../../../../../src/SdkConfig"; -import dispatcher from "../../../../../src/dispatcher/dispatcher"; -import { CallStore } from "../../../../../src/stores/CallStore"; -import { Call, ElementCall } from "../../../../../src/models/Call"; -import * as ShieldUtils from "../../../../../src/utils/ShieldUtils"; -import { Container, WidgetLayoutStore } from "../../../../../src/stores/widgets/WidgetLayoutStore"; -import MatrixClientContext from "../../../../../src/contexts/MatrixClientContext"; -import { _t } from "../../../../../src/languageHandler"; -import * as UseCall from "../../../../../src/hooks/useCall"; -import { SdkContextClass } from "../../../../../src/contexts/SDKContext"; -import WidgetStore, { IApp } from "../../../../../src/stores/WidgetStore"; -import { UIFeature } from "../../../../../src/settings/UIFeature"; +import { filterConsole, stubClient } from "../../../../../test-utils"; +import RoomHeader from "../../../../../../src/components/views/rooms/RoomHeader/RoomHeader"; +import DMRoomMap from "../../../../../../src/utils/DMRoomMap"; +import { MatrixClientPeg } from "../../../../../../src/MatrixClientPeg"; +import RightPanelStore from "../../../../../../src/stores/right-panel/RightPanelStore"; +import { RightPanelPhases } from "../../../../../../src/stores/right-panel/RightPanelStorePhases"; +import LegacyCallHandler from "../../../../../../src/LegacyCallHandler"; +import SettingsStore from "../../../../../../src/settings/SettingsStore"; +import SdkConfig from "../../../../../../src/SdkConfig"; +import dispatcher from "../../../../../../src/dispatcher/dispatcher"; +import { CallStore } from "../../../../../../src/stores/CallStore"; +import { Call, ElementCall } from "../../../../../../src/models/Call"; +import * as ShieldUtils from "../../../../../../src/utils/ShieldUtils"; +import { Container, WidgetLayoutStore } from "../../../../../../src/stores/widgets/WidgetLayoutStore"; +import MatrixClientContext from "../../../../../../src/contexts/MatrixClientContext"; +import { _t } from "../../../../../../src/languageHandler"; +import * as UseCall from "../../../../../../src/hooks/useCall"; +import { SdkContextClass } from "../../../../../../src/contexts/SDKContext"; +import WidgetStore, { IApp } from "../../../../../../src/stores/WidgetStore"; +import { UIFeature } from "../../../../../../src/settings/UIFeature"; jest.mock("../../../../../src/utils/ShieldUtils"); From 4eace4f15d0e6dd5f6a9aa0b94df01d35f85f3da Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Mon, 13 Jan 2025 01:05:17 +0530 Subject: [PATCH 07/11] Style face pile for toggle state --- res/css/views/rooms/_RoomHeader.pcss | 7 +++++++ src/components/views/elements/FacePile.tsx | 11 ++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/res/css/views/rooms/_RoomHeader.pcss b/res/css/views/rooms/_RoomHeader.pcss index 66b6bdde307..ac2e419e0d2 100644 --- a/res/css/views/rooms/_RoomHeader.pcss +++ b/res/css/views/rooms/_RoomHeader.pcss @@ -71,6 +71,7 @@ Please see LICENSE files in the repository root for full details. padding: var(--cpd-space-1-5x); cursor: pointer; user-select: none; + font: var(--cpd-font-body-sm-medium); /* RoomAvatar doesn't pass classes down to avatar So set style here @@ -83,6 +84,12 @@ Please see LICENSE files in the repository root for full details. color: $primary-content; background: var(--cpd-color-bg-subtle-primary); } + + &.mx_FacePile_toggled { + background: var(--cpd-color-bg-success-subtle); + color: var(--cpd-color-text-action-accent); + font: var(--cpd-font-body-sm-semibold); + } } .mx_RoomHeader .mx_BaseAvatar { diff --git a/src/components/views/elements/FacePile.tsx b/src/components/views/elements/FacePile.tsx index 49416a8d381..52836a205e2 100644 --- a/src/components/views/elements/FacePile.tsx +++ b/src/components/views/elements/FacePile.tsx @@ -9,9 +9,12 @@ Please see LICENSE files in the repository root for full details. import React, { FC, HTMLAttributes, ReactNode } from "react"; import { RoomMember } from "matrix-js-sdk/src/matrix"; import { AvatarStack, Tooltip } from "@vector-im/compound-web"; +import classNames from "classnames"; import MemberAvatar from "../avatars/MemberAvatar"; import AccessibleButton, { ButtonEvent } from "./AccessibleButton"; +import { useToggled } from "../rooms/RoomHeader/highlight/useToggled"; +import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases"; interface IProps extends Omit, "onChange"> { members: RoomMember[]; @@ -57,8 +60,14 @@ const FacePile: FC = ({ ); + const toggled = useToggled(RightPanelPhases.MemberList); + const classes = classNames({ + mx_FacePile: true, + mx_FacePile_toggled: toggled, + }); + const content = ( - + {pileContents} {children} From 745892a79eb5e8d18521cde37e3f5dc51cb8d38d Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Mon, 13 Jan 2025 02:51:54 +0530 Subject: [PATCH 08/11] Fix broken CI --- .../rooms/RoomHeader/highlight/useToggled.tsx | 2 +- .../__snapshots__/RoomView-test.tsx.snap | 15 +++++++++++++++ .../views/rooms/RoomHeader/RoomHeader-test.tsx | 7 ++++++- .../__snapshots__/RoomHeader-test.tsx.snap | 2 ++ .../VideoRoomChatButton-test.tsx.snap | 1 + 5 files changed, 25 insertions(+), 2 deletions(-) rename test/unit-tests/components/views/rooms/{ => RoomHeader}/__snapshots__/RoomHeader-test.tsx.snap (99%) diff --git a/src/components/views/rooms/RoomHeader/highlight/useToggled.tsx b/src/components/views/rooms/RoomHeader/highlight/useToggled.tsx index 28f36fe0f98..61908edcc67 100644 --- a/src/components/views/rooms/RoomHeader/highlight/useToggled.tsx +++ b/src/components/views/rooms/RoomHeader/highlight/useToggled.tsx @@ -16,7 +16,7 @@ import { CurrentRightPanelPhaseContext } from "../../../../../contexts/CurrentRi export function useToggled(phase: RightPanelPhases): boolean { const context = useContext(CurrentRightPanelPhaseContext); if (!context) { - throw new Error("Context is null, did you forget to use CurrentRightPanelPhaseContextProvider?"); + return false; } const { currentPhase, isPanelOpen } = context; return !!(isPanelOpen && currentPhase === phase); diff --git a/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap b/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap index 65a755058bf..eb775bdcbec 100644 --- a/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap +++ b/test/unit-tests/components/structures/__snapshots__/RoomView-test.tsx.snap @@ -110,6 +110,7 @@ exports[`RoomView for a local room in state CREATING should match the snapshot 1 style="--cpd-icon-button-size: 100%;" > ({ + useCurrentPhase: () => { + return { currentPhase: "foo", isOpen: false }; + }, +})); function getWrapper(): RenderOptions { return { diff --git a/test/unit-tests/components/views/rooms/__snapshots__/RoomHeader-test.tsx.snap b/test/unit-tests/components/views/rooms/RoomHeader/__snapshots__/RoomHeader-test.tsx.snap similarity index 99% rename from test/unit-tests/components/views/rooms/__snapshots__/RoomHeader-test.tsx.snap rename to test/unit-tests/components/views/rooms/RoomHeader/__snapshots__/RoomHeader-test.tsx.snap index 3db3fb67fbf..6d0c2dc3e4a 100644 --- a/test/unit-tests/components/views/rooms/__snapshots__/RoomHeader-test.tsx.snap +++ b/test/unit-tests/components/views/rooms/RoomHeader/__snapshots__/RoomHeader-test.tsx.snap @@ -105,6 +105,7 @@ exports[`RoomHeader dm does not show the face pile for DMs 1`] = ` style="--cpd-icon-button-size: 100%;" > renders button with an unread marker when room style="--cpd-icon-button-size: 100%;" > Date: Mon, 13 Jan 2025 03:19:29 +0530 Subject: [PATCH 09/11] Give directory a better name --- src/components/views/elements/FacePile.tsx | 2 +- src/components/views/rooms/RoomHeader/RoomHeader.tsx | 2 +- src/components/views/rooms/RoomHeader/VideoRoomChatButton.tsx | 2 +- .../rooms/RoomHeader/{highlight => toggle}/ToggleableIcon.tsx | 0 .../views/rooms/RoomHeader/{highlight => toggle}/useToggled.tsx | 0 5 files changed, 3 insertions(+), 3 deletions(-) rename src/components/views/rooms/RoomHeader/{highlight => toggle}/ToggleableIcon.tsx (100%) rename src/components/views/rooms/RoomHeader/{highlight => toggle}/useToggled.tsx (100%) diff --git a/src/components/views/elements/FacePile.tsx b/src/components/views/elements/FacePile.tsx index 52836a205e2..5eecd9a3ba7 100644 --- a/src/components/views/elements/FacePile.tsx +++ b/src/components/views/elements/FacePile.tsx @@ -13,7 +13,7 @@ import classNames from "classnames"; import MemberAvatar from "../avatars/MemberAvatar"; import AccessibleButton, { ButtonEvent } from "./AccessibleButton"; -import { useToggled } from "../rooms/RoomHeader/highlight/useToggled"; +import { useToggled } from "../rooms/RoomHeader/toggle/useToggled"; import { RightPanelPhases } from "../../../stores/right-panel/RightPanelStorePhases"; interface IProps extends Omit, "onChange"> { diff --git a/src/components/views/rooms/RoomHeader/RoomHeader.tsx b/src/components/views/rooms/RoomHeader/RoomHeader.tsx index 5f8bf22d17f..206b7f24133 100644 --- a/src/components/views/rooms/RoomHeader/RoomHeader.tsx +++ b/src/components/views/rooms/RoomHeader/RoomHeader.tsx @@ -52,7 +52,7 @@ import { MainSplitContentType } from "../../../structures/RoomView.tsx"; import defaultDispatcher from "../../../../dispatcher/dispatcher.ts"; import { RoomSettingsTab } from "../../dialogs/RoomSettingsDialog.tsx"; import { useScopedRoomContext } from "../../../../contexts/ScopedRoomContext.tsx"; -import { ToggleableIcon } from "./highlight/ToggleableIcon.tsx"; +import { ToggleableIcon } from "./toggle/ToggleableIcon.tsx"; import { CurrentRightPanelPhaseContextProvider } from "../../../../contexts/CurrentRightPanelPhaseContext.tsx"; export default function RoomHeader({ diff --git a/src/components/views/rooms/RoomHeader/VideoRoomChatButton.tsx b/src/components/views/rooms/RoomHeader/VideoRoomChatButton.tsx index 72061b52495..b1b8ae071e2 100644 --- a/src/components/views/rooms/RoomHeader/VideoRoomChatButton.tsx +++ b/src/components/views/rooms/RoomHeader/VideoRoomChatButton.tsx @@ -18,7 +18,7 @@ import { NotificationLevel } from "../../../../stores/notifications/Notification import { RightPanelPhases } from "../../../../stores/right-panel/RightPanelStorePhases"; import { SDKContext } from "../../../../contexts/SDKContext"; import { ButtonEvent } from "../../elements/AccessibleButton"; -import { ToggleableIcon } from "./highlight/ToggleableIcon"; +import { ToggleableIcon } from "./toggle/ToggleableIcon"; /** * Display a button to toggle timeline for video rooms diff --git a/src/components/views/rooms/RoomHeader/highlight/ToggleableIcon.tsx b/src/components/views/rooms/RoomHeader/toggle/ToggleableIcon.tsx similarity index 100% rename from src/components/views/rooms/RoomHeader/highlight/ToggleableIcon.tsx rename to src/components/views/rooms/RoomHeader/toggle/ToggleableIcon.tsx diff --git a/src/components/views/rooms/RoomHeader/highlight/useToggled.tsx b/src/components/views/rooms/RoomHeader/toggle/useToggled.tsx similarity index 100% rename from src/components/views/rooms/RoomHeader/highlight/useToggled.tsx rename to src/components/views/rooms/RoomHeader/toggle/useToggled.tsx From 37931cff8555ad8a0cba53d98afb3378ce6a66b4 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Mon, 27 Jan 2025 15:41:37 +0530 Subject: [PATCH 10/11] Update year in license --- src/components/views/rooms/RoomHeader/toggle/ToggleableIcon.tsx | 2 +- src/components/views/rooms/RoomHeader/toggle/useToggled.tsx | 2 +- src/contexts/CurrentRightPanelPhaseContext.tsx | 2 +- src/hooks/right-panel/useCurrentPhase.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/RoomHeader/toggle/ToggleableIcon.tsx b/src/components/views/rooms/RoomHeader/toggle/ToggleableIcon.tsx index 4a5f1a73959..93406419c74 100644 --- a/src/components/views/rooms/RoomHeader/toggle/ToggleableIcon.tsx +++ b/src/components/views/rooms/RoomHeader/toggle/ToggleableIcon.tsx @@ -1,5 +1,5 @@ /* -Copyright 2024 New Vector Ltd. +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. diff --git a/src/components/views/rooms/RoomHeader/toggle/useToggled.tsx b/src/components/views/rooms/RoomHeader/toggle/useToggled.tsx index 61908edcc67..9d3a5ed0cb5 100644 --- a/src/components/views/rooms/RoomHeader/toggle/useToggled.tsx +++ b/src/components/views/rooms/RoomHeader/toggle/useToggled.tsx @@ -1,5 +1,5 @@ /* -Copyright 2024 New Vector Ltd. +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. diff --git a/src/contexts/CurrentRightPanelPhaseContext.tsx b/src/contexts/CurrentRightPanelPhaseContext.tsx index 5526f59a748..1ab3eafc25a 100644 --- a/src/contexts/CurrentRightPanelPhaseContext.tsx +++ b/src/contexts/CurrentRightPanelPhaseContext.tsx @@ -1,5 +1,5 @@ /* -Copyright 2024 New Vector Ltd. +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. diff --git a/src/hooks/right-panel/useCurrentPhase.ts b/src/hooks/right-panel/useCurrentPhase.ts index 48746f97147..9560612873d 100644 --- a/src/hooks/right-panel/useCurrentPhase.ts +++ b/src/hooks/right-panel/useCurrentPhase.ts @@ -1,5 +1,5 @@ /* -Copyright 2024 New Vector Ltd. +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. From 7bd30335b6ff232eb3251bde5cf3470bb53ed990 Mon Sep 17 00:00:00 2001 From: R Midhun Suresh Date: Mon, 27 Jan 2025 16:19:56 +0530 Subject: [PATCH 11/11] Use a stronger type --- src/components/views/rooms/RoomHeader/toggle/ToggleableIcon.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomHeader/toggle/ToggleableIcon.tsx b/src/components/views/rooms/RoomHeader/toggle/ToggleableIcon.tsx index 93406419c74..f90679b7442 100644 --- a/src/components/views/rooms/RoomHeader/toggle/ToggleableIcon.tsx +++ b/src/components/views/rooms/RoomHeader/toggle/ToggleableIcon.tsx @@ -12,7 +12,7 @@ import { RightPanelPhases } from "../../../../../stores/right-panel/RightPanelSt import { useToggled } from "./useToggled"; type Props = { - Icon: React.ComponentType; + Icon: React.ComponentType>; phase: RightPanelPhases; };