Files
element-web/src/hooks/useEventEmitter.ts
Robin d077ea1990 New group call experience: Room header call buttons (#9311)
* Make useEventEmitterState more efficient

By not invoking the initializing function on every render

* Make useWidgets more efficient

By not calling WidgetStore on every render

* Add new group call experience Labs flag

* Add viewingCall field to RoomViewStore state

Currently has no effect, but in the future this will signal to RoomView to show the call or call lobby.

* Add element_call.use_exclusively config flag

As documented in element-web, this will tell the app to use Element Call exclusively for calls, disabling Jitsi and legacy 1:1 calls.

* Make placeCall return a promise

So that the UI can know when placeCall completes

* Update start call buttons to new group call designs

Since RoomView doesn't do anything with viewingCall yet, these buttons won't have any effect when starting native group calls, but the logic is at least all there and ready to be hooked up.

* Allow calls to be detected if the new group call experience is enabled

* Test the RoomHeader changes

* Iterate code
2022-09-25 10:57:25 -04:00

99 lines
2.9 KiB
TypeScript

/*
Copyright 2019 The Matrix.org Foundation C.I.C.
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 { useRef, useEffect, useState, useCallback } from "react";
import { ListenerMap, TypedEventEmitter } from "matrix-js-sdk/src/models/typed-event-emitter";
import type { EventEmitter } from "events";
type Handler = (...args: any[]) => void;
export function useTypedEventEmitter<
Events extends string,
Arguments extends ListenerMap<Events>,
>(
emitter: TypedEventEmitter<Events, Arguments>,
eventName: Events,
handler: Handler,
): void {
useEventEmitter(emitter, eventName, handler);
}
/**
* Hook to wrap an EventEmitter on and off in hook lifecycle
*/
export function useEventEmitter(
emitter: EventEmitter | undefined,
eventName: string | symbol,
handler: Handler,
): void {
// Create a ref that stores handler
const savedHandler = useRef(handler);
// Update ref.current value if handler changes.
useEffect(() => {
savedHandler.current = handler;
}, [handler]);
useEffect(
() => {
// allow disabling this hook by passing a falsy emitter
if (!emitter) return;
// Create event listener that calls handler function stored in ref
const eventListener = (...args) => savedHandler.current(...args);
// Add event listener
emitter.on(eventName, eventListener);
// Remove event listener on cleanup
return () => {
emitter.off(eventName, eventListener);
};
},
[eventName, emitter], // Re-run if eventName or emitter changes
);
}
type Mapper<T> = (...args: any[]) => T;
export function useTypedEventEmitterState<
T,
Events extends string,
Arguments extends ListenerMap<Events>,
>(
emitter: TypedEventEmitter<Events, Arguments>,
eventName: Events,
fn: Mapper<T>,
): T {
return useEventEmitterState<T>(emitter, eventName, fn);
}
export function useEventEmitterState<T>(
emitter: EventEmitter | undefined,
eventName: string | symbol,
fn: Mapper<T>,
): T {
const [value, setValue] = useState<T>(fn);
const handler = useCallback((...args: any[]) => {
setValue(fn(...args));
}, [fn]);
// re-run when the emitter changes
useEffect(handler, [emitter]); // eslint-disable-line react-hooks/exhaustive-deps
useEventEmitter(emitter, eventName, handler);
return value;
}