diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index c1710e2..ed96b97 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 "./useJoinRule"; import { useUrlParams } from "../UrlParams"; const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {}); @@ -116,6 +118,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 @@ -346,7 +350,7 @@ export function InCallView({ inCall roomIdOrAlias={roomIdOrAlias} groupCall={groupCall} - showInvite={true} + showInvite={joinRule === JoinRule.Public} feedbackModalState={feedbackModalState} feedbackModalProps={feedbackModalProps} /> 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 index 092bd73..4c4d015 100644 --- a/src/room/useRoomAvatar.ts +++ b/src/room/useRoomAvatar.ts @@ -1,24 +1,26 @@ -import { useState, useEffect } from "react"; -import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +/* +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 { 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()); +import { useRoomState } from "./useRoomState"; - 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; -}; +export const useRoomAvatar = (room: Room) => + useRoomState( + room, + useCallback(() => room.getMxcAvatarUrl(), [room]) + ); diff --git a/src/room/useRoomState.ts b/src/room/useRoomState.ts new file mode 100644 index 0000000..2fa4d0b --- /dev/null +++ b/src/room/useRoomState.ts @@ -0,0 +1,39 @@ +/* +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]); +}; 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]); +};