From 02b2aef958a45118577738e5832c4f212396c197 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Fri, 14 Oct 2022 09:40:21 -0400 Subject: [PATCH 1/2] Hide the invite button in non-public rooms --- src/room/GroupCallView.tsx | 2 +- src/room/InCallView.tsx | 6 ++++- src/room/useRoomAvatar.ts | 24 ------------------ src/room/useRoomState.ts | 51 ++++++++++++++++++++++++++++++++++++++ src/useEvents.ts | 24 +++++++++++++++++- 5 files changed, 80 insertions(+), 27 deletions(-) delete mode 100644 src/room/useRoomAvatar.ts create mode 100644 src/room/useRoomState.ts diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index 8bf6a13..c508220 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -29,7 +29,7 @@ import { LobbyView } from "./LobbyView"; import { InCallView } from "./InCallView"; import { PTTCallView } from "./PTTCallView"; import { CallEndedView } from "./CallEndedView"; -import { useRoomAvatar } from "./useRoomAvatar"; +import { useRoomAvatar } from "./useRoomState"; import { useSentryGroupCallHandler } from "./useSentryGroupCallHandler"; import { useLocationNavigation } from "../useLocationNavigation"; import { useMediaHandler } from "../settings/useMediaHandler"; diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 03c95fd..86cb29c 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -24,6 +24,7 @@ import { GroupCall } from "matrix-js-sdk/src/webrtc/groupCall"; import { CallFeed } from "matrix-js-sdk/src/webrtc/callFeed"; import classNames from "classnames"; import { useTranslation } from "react-i18next"; +import { JoinRule } from "matrix-js-sdk/src/@types/partials"; import type { IWidgetApiRequest } from "matrix-widget-api"; import styles from "./InCallView.module.css"; @@ -57,6 +58,7 @@ import { useFullscreen } from "../video-grid/useFullscreen"; import { AudioContainer } from "../video-grid/AudioContainer"; import { useAudioOutputDevice } from "../video-grid/useAudioOutputDevice"; import { widget, ElementWidgetActions } from "../widget"; +import { useJoinRule } from "./useRoomState"; const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {}); // There is currently a bug in Safari our our code with cloning and sending MediaStreams @@ -115,6 +117,8 @@ export function InCallView({ }: Props) { const { t } = useTranslation(); usePreventScroll(); + const joinRule = useJoinRule(groupCall.room); + const containerRef1 = useRef(null); const [containerRef2, bounds] = useMeasure({ polyfill: ResizeObserver }); // Merge the refs so they can attach to the same element @@ -340,7 +344,7 @@ export function InCallView({ inCall roomIdOrAlias={roomIdOrAlias} groupCall={groupCall} - showInvite={true} + showInvite={joinRule === JoinRule.Public} feedbackModalState={feedbackModalState} feedbackModalProps={feedbackModalProps} /> diff --git a/src/room/useRoomAvatar.ts b/src/room/useRoomAvatar.ts deleted file mode 100644 index 092bd73..0000000 --- a/src/room/useRoomAvatar.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useState, useEffect } from "react"; -import { MatrixEvent } from "matrix-js-sdk/src/models/event"; -import { Room } from "matrix-js-sdk/src/models/room"; -import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; -import { EventType } from "matrix-js-sdk/src/@types/event"; - -export const useRoomAvatar = (room: Room) => { - const [avatarUrl, setAvatarUrl] = useState(room.getMxcAvatarUrl()); - - useEffect(() => { - const update = (ev: MatrixEvent) => { - if (ev.getType() === EventType.RoomAvatar) { - setAvatarUrl(room.getMxcAvatarUrl()); - } - }; - - room.currentState.on(RoomStateEvent.Events, update); - return () => { - room.currentState.off(RoomStateEvent.Events, update); - }; - }, [room]); - - return avatarUrl; -}; diff --git a/src/room/useRoomState.ts b/src/room/useRoomState.ts new file mode 100644 index 0000000..5f23794 --- /dev/null +++ b/src/room/useRoomState.ts @@ -0,0 +1,51 @@ +/* +Copyright 2022 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { RoomState, RoomStateEvent } from "matrix-js-sdk/src/models/room-state"; +import { useCallback, useMemo, useState } from "react"; + +import type { Room } from "matrix-js-sdk/src/models/room"; +import { useTypedEventEmitter } from "../useEvents"; + +/** + * A React hook for values computed from room state. + * @param room The room. + * @param f A mapping from the current room state to the computed value. + * @returns The computed value. + */ +export const useRoomState = (room: Room, f: (state: RoomState) => T): T => { + const [numUpdates, setNumUpdates] = useState(0); + useTypedEventEmitter( + room, + RoomStateEvent.Update, + useCallback(() => setNumUpdates((n) => n + 1), [setNumUpdates]) + ); + // We want any change to the update counter to trigger an update here + // eslint-disable-next-line react-hooks/exhaustive-deps + return useMemo(() => f(room.currentState), [room, f, numUpdates]); +}; + +export const useRoomAvatar = (room: Room) => + useRoomState( + room, + useCallback(() => room.getMxcAvatarUrl(), [room]) + ); + +export const useJoinRule = (room: Room) => + useRoomState( + room, + useCallback((state) => state.getJoinRule(), []) + ); diff --git a/src/useEvents.ts b/src/useEvents.ts index b08f9ff..6f1332b 100644 --- a/src/useEvents.ts +++ b/src/useEvents.ts @@ -16,6 +16,12 @@ limitations under the License. import { useEffect } from "react"; +import type { + Listener, + ListenerMap, + TypedEventEmitter, +} from "matrix-js-sdk/src/models/typed-event-emitter"; + // Shortcut for registering a listener on an EventTarget export const useEventTarget = ( target: EventTarget, @@ -31,4 +37,20 @@ export const useEventTarget = ( }, [target, eventType, listener, options]); }; -// TODO: Have a similar hook for EventEmitters +// Shortcut for registering a listener on a TypedEventEmitter +export const useTypedEventEmitter = < + Events extends string, + Arguments extends ListenerMap, + T extends Events +>( + emitter: TypedEventEmitter, + eventType: T, + listener: Listener +) => { + useEffect(() => { + emitter.on(eventType, listener); + return () => { + emitter.off(eventType, listener); + }; + }, [emitter, eventType, listener]); +}; From 5ed2dc6e0e34517f021a6e15e8d3db1a962673cc Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Fri, 14 Oct 2022 10:50:36 -0400 Subject: [PATCH 2/2] Split room state hooks out into separate files --- src/room/GroupCallView.tsx | 2 +- src/room/InCallView.tsx | 2 +- src/room/useJoinRule.ts | 26 ++++++++++++++++++++++++++ src/room/useRoomAvatar.ts | 26 ++++++++++++++++++++++++++ src/room/useRoomState.ts | 12 ------------ 5 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 src/room/useJoinRule.ts create mode 100644 src/room/useRoomAvatar.ts diff --git a/src/room/GroupCallView.tsx b/src/room/GroupCallView.tsx index c508220..8bf6a13 100644 --- a/src/room/GroupCallView.tsx +++ b/src/room/GroupCallView.tsx @@ -29,7 +29,7 @@ import { LobbyView } from "./LobbyView"; import { InCallView } from "./InCallView"; import { PTTCallView } from "./PTTCallView"; import { CallEndedView } from "./CallEndedView"; -import { useRoomAvatar } from "./useRoomState"; +import { useRoomAvatar } from "./useRoomAvatar"; import { useSentryGroupCallHandler } from "./useSentryGroupCallHandler"; import { useLocationNavigation } from "../useLocationNavigation"; import { useMediaHandler } from "../settings/useMediaHandler"; diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 86cb29c..e6272ca 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -58,7 +58,7 @@ import { useFullscreen } from "../video-grid/useFullscreen"; import { AudioContainer } from "../video-grid/AudioContainer"; import { useAudioOutputDevice } from "../video-grid/useAudioOutputDevice"; import { widget, ElementWidgetActions } from "../widget"; -import { useJoinRule } from "./useRoomState"; +import { useJoinRule } from "./useJoinRule"; const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {}); // There is currently a bug in Safari our our code with cloning and sending MediaStreams diff --git a/src/room/useJoinRule.ts b/src/room/useJoinRule.ts new file mode 100644 index 0000000..78abc50 --- /dev/null +++ b/src/room/useJoinRule.ts @@ -0,0 +1,26 @@ +/* +Copyright 2022 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { useCallback } from "react"; + +import type { Room } from "matrix-js-sdk/src/models/room"; +import { useRoomState } from "./useRoomState"; + +export const useJoinRule = (room: Room) => + useRoomState( + room, + useCallback((state) => state.getJoinRule(), []) + ); diff --git a/src/room/useRoomAvatar.ts b/src/room/useRoomAvatar.ts new file mode 100644 index 0000000..4c4d015 --- /dev/null +++ b/src/room/useRoomAvatar.ts @@ -0,0 +1,26 @@ +/* +Copyright 2022 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import { useCallback } from "react"; +import { Room } from "matrix-js-sdk/src/models/room"; + +import { useRoomState } from "./useRoomState"; + +export const useRoomAvatar = (room: Room) => + useRoomState( + room, + useCallback(() => room.getMxcAvatarUrl(), [room]) + ); diff --git a/src/room/useRoomState.ts b/src/room/useRoomState.ts index 5f23794..2fa4d0b 100644 --- a/src/room/useRoomState.ts +++ b/src/room/useRoomState.ts @@ -37,15 +37,3 @@ export const useRoomState = (room: Room, f: (state: RoomState) => T): T => { // eslint-disable-next-line react-hooks/exhaustive-deps return useMemo(() => f(room.currentState), [room, f, numUpdates]); }; - -export const useRoomAvatar = (room: Room) => - useRoomState( - room, - useCallback(() => room.getMxcAvatarUrl(), [room]) - ); - -export const useJoinRule = (room: Room) => - useRoomState( - room, - useCallback((state) => state.getJoinRule(), []) - );