communication-react-19
Version:
React library for building modern communication user experiences utilizing Azure Communication Services (React 19 compatible fork)
81 lines • 3.95 kB
JavaScript
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
/* eslint-disable @typescript-eslint/no-explicit-any */
import { useState, useEffect, useRef } from 'react';
import memoizeOne from 'memoize-one';
import { useAdapter } from '../adapter/CallAdapterProvider';
/**
* @private
*/
export const useAdaptedSelector = (selector, selectorProps) => {
return useSelectorWithAdaptation(selector, adaptCompositeState, selectorProps);
};
/**
* @private
*/
export const useSelectorWithAdaptation = (selector, adaptState, selectorProps) => {
var _a;
const adapter = useAdapter();
// Keeps track of whether the current component is mounted or not. If it has unmounted, make sure we do not modify the
// state or it will cause React warnings in the console. https://skype.visualstudio.com/SPOOL/_workitems/edit/2453212
const mounted = useRef(false);
useEffect(() => {
mounted.current = true;
return () => {
mounted.current = false;
};
});
const callId = (_a = adapter.getState().call) === null || _a === void 0 ? void 0 : _a.id;
const [props, setProps] = useState(selector(adaptState(adapter.getState()), selectorProps !== null && selectorProps !== void 0 ? selectorProps : { callId }));
const propRef = useRef(props);
propRef.current = props;
useEffect(() => {
const onStateChange = (state) => {
var _a;
if (!mounted.current) {
return;
}
const newProps = selector(adaptState(state), selectorProps !== null && selectorProps !== void 0 ? selectorProps : { callId: (_a = state.call) === null || _a === void 0 ? void 0 : _a.id });
if (propRef.current !== newProps) {
setProps(newProps);
}
};
adapter.onStateChange(onStateChange);
return () => {
adapter.offStateChange(onStateChange);
};
}, [adaptState, adapter, selector, selectorProps]);
return props;
};
const memoizeState = memoizeOne((userId, deviceManager, calls, latestErrors, latestNotifications, displayName, alternateCallerId, environmentInfo) => ({
userId,
incomingCalls: {},
incomingCallsEnded: {},
callsEnded: {},
deviceManager,
callAgent: { displayName },
calls,
latestErrors,
latestNotifications: latestNotifications !== null && latestNotifications !== void 0 ? latestNotifications : {},
alternateCallerId,
environmentInfo
}));
const memoizeCalls = memoizeOne((call) => (call ? { [call.id]: call } : {}));
const adaptCompositeState = (compositeState) => {
return memoizeState(compositeState.userId, compositeState.devices, memoizeCalls(compositeState.call),
// This is an unsafe type expansion.
// compositeState.latestErrors can contain properties that are not valid in CallErrors.
//
// But there is no way to check for valid property names at runtime:
// - The set of valid property names is built from types in the @azure/communication-calling.
// Thus we don't have a literal array of allowed strings at runtime.
// - Due to minification / uglification, the property names from the objects at runtime can't be used
// to compare against permissible values inferred from the types.
//
// This is not a huge problem -- it simply means that our adapted selector will include some extra operations
// that are unknown to the UI component and data binding libraries. Generic handling of the errors (e.g.,
// just displaying them in some UI surface) will continue to work for these operations. Handling of
// specific operations (e.g., acting on errors related to permission issues) will ignore these operations.
compositeState.latestErrors, compositeState.latestNotifications, compositeState.displayName, compositeState.alternateCallerId, compositeState.environmentInfo);
};
//# sourceMappingURL=useAdaptedSelector.js.map