@azure/communication-react 1.4.2-alpha-202211240014.0 → 1.4.2-alpha-202211290015.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/CHANGELOG.beta.md +47 -1
  2. package/CHANGELOG.json +507 -0
  3. package/dist/communication-react.d.ts +48 -0
  4. package/dist/dist-cjs/communication-react/index.js +258 -87
  5. package/dist/dist-cjs/communication-react/index.js.map +1 -1
  6. package/dist/dist-esm/acs-ui-common/src/telemetryVersion.js +1 -1
  7. package/dist/dist-esm/acs-ui-common/src/telemetryVersion.js.map +1 -1
  8. package/dist/dist-esm/react-components/src/components/Dialpad/Dialpad.js +12 -9
  9. package/dist/dist-esm/react-components/src/components/Dialpad/Dialpad.js.map +1 -1
  10. package/dist/dist-esm/react-components/src/components/VideoTile.d.ts +4 -0
  11. package/dist/dist-esm/react-components/src/components/VideoTile.js +25 -2
  12. package/dist/dist-esm/react-components/src/components/VideoTile.js.map +1 -1
  13. package/dist/dist-esm/react-components/src/components/utils/useLongPress.d.ts +12 -10
  14. package/dist/dist-esm/react-components/src/components/utils/useLongPress.js +61 -37
  15. package/dist/dist-esm/react-components/src/components/utils/useLongPress.js.map +1 -1
  16. package/dist/dist-esm/react-composites/src/composites/CallComposite/Strings.d.ts +44 -0
  17. package/dist/dist-esm/react-composites/src/composites/CallComposite/Strings.js.map +1 -1
  18. package/dist/dist-esm/react-composites/src/composites/CallComposite/components/MediaGallery.js +6 -2
  19. package/dist/dist-esm/react-composites/src/composites/CallComposite/components/MediaGallery.js.map +1 -1
  20. package/dist/dist-esm/react-composites/src/composites/CallComposite/selectors/mediaGallerySelector.d.ts +7 -0
  21. package/dist/dist-esm/react-composites/src/composites/CallComposite/selectors/mediaGallerySelector.js +9 -1
  22. package/dist/dist-esm/react-composites/src/composites/CallComposite/selectors/mediaGallerySelector.js.map +1 -1
  23. package/dist/dist-esm/react-composites/src/composites/CallComposite/utils/MediaGalleryUtils.d.ts +29 -0
  24. package/dist/dist-esm/react-composites/src/composites/CallComposite/utils/MediaGalleryUtils.js +119 -0
  25. package/dist/dist-esm/react-composites/src/composites/CallComposite/utils/MediaGalleryUtils.js.map +1 -0
  26. package/dist/dist-esm/react-composites/src/composites/localization/locales/en-US/strings.json +12 -1
  27. package/package.json +8 -8
@@ -1,7 +1,7 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT license.
3
3
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
4
- import { VideoGallery } from "../../../../../react-components/src";
4
+ import { VideoGallery, Announcer } from "../../../../../react-components/src";
5
5
  import { usePropsFor } from '../hooks/usePropsFor';
6
6
  import { AvatarPersona } from '../../common/AvatarPersona';
7
7
  import { mergeStyles, Stack } from '@fluentui/react';
@@ -10,6 +10,7 @@ import { useHandlers } from '../hooks/useHandlers';
10
10
  import { useSelector } from '../hooks/useSelector';
11
11
  import { localVideoCameraCycleButtonSelector } from '../selectors/LocalVideoTileSelector';
12
12
  import { LocalVideoCameraCycleButton } from "../../../../../react-components/src";
13
+ import { useParticipantChangedAnnouncement } from '../utils/MediaGalleryUtils';
13
14
  const VideoGalleryStyles = {
14
15
  root: {
15
16
  height: '100%',
@@ -31,6 +32,7 @@ export const MediaGallery = (props) => {
31
32
  const videoGalleryProps = usePropsFor(VideoGallery);
32
33
  const cameraSwitcherCameras = useSelector(localVideoCameraCycleButtonSelector);
33
34
  const cameraSwitcherCallback = useHandlers(LocalVideoCameraCycleButton);
35
+ const announcerString = useParticipantChangedAnnouncement();
34
36
  const cameraSwitcherProps = useMemo(() => {
35
37
  return Object.assign(Object.assign({}, cameraSwitcherCallback), cameraSwitcherCameras);
36
38
  }, [cameraSwitcherCallback, cameraSwitcherCameras]);
@@ -43,7 +45,9 @@ export const MediaGallery = (props) => {
43
45
  const VideoGalleryMemoized = useMemo(() => {
44
46
  return (React.createElement(VideoGallery, Object.assign({}, videoGalleryProps, { localVideoViewOptions: localVideoViewOptions, remoteVideoViewOptions: remoteVideoViewOptions, styles: VideoGalleryStyles, layout: "floatingLocalVideo", showCameraSwitcherInLocalPreview: props.isMobile, localVideoCameraCycleButtonProps: cameraSwitcherProps, onRenderAvatar: onRenderAvatar })));
45
47
  }, [videoGalleryProps, props.isMobile, onRenderAvatar, cameraSwitcherProps]);
46
- return VideoGalleryMemoized;
48
+ return (React.createElement(React.Fragment, null,
49
+ React.createElement(Announcer, { announcementString: announcerString, ariaLive: 'polite' }),
50
+ VideoGalleryMemoized));
47
51
  };
48
52
  /**
49
53
  * @private
@@ -1 +1 @@
1
- {"version":3,"file":"MediaGallery.js","sourceRoot":"","sources":["../../../../../../../../react-composites/src/composites/CallComposite/components/MediaGallery.tsx"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC,OAAO,KAAK,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACzE,OAAO,EACL,YAAY,EAIb,4CAAmC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,aAAa,EAA6B,MAAM,4BAA4B,CAAC;AACtF,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,mCAAmC,EAAE,MAAM,qCAAqC,CAAC;AAC1F,OAAO,EAAE,2BAA2B,EAAE,4CAAmC;AAEzE,MAAM,kBAAkB,GAAG;IACzB,IAAI,EAAE;QACJ,MAAM,EAAE,MAAM;QACd,SAAS,EAAE,OAAO;QAClB,QAAQ,EAAE,MAAM;KACjB;CACF,CAAC;AAEF,MAAM,qBAAqB,GAAG;IAC5B,WAAW,EAAE,MAAM;IACnB,UAAU,EAAE,IAAI;CACK,CAAC;AAExB,MAAM,sBAAsB,GAAG;IAC7B,WAAW,EAAE,MAAM;CACE,CAAC;AAcxB;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,KAAwB,EAAe,EAAE;IACpE,MAAM,iBAAiB,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC;IACpD,MAAM,qBAAqB,GAAG,WAAW,CAAC,mCAAmC,CAAC,CAAC;IAC/E,MAAM,sBAAsB,GAAG,WAAW,CAAC,2BAA2B,CAAC,CAAC;IACxE,MAAM,mBAAmB,GAAG,OAAO,CAAC,GAAG,EAAE;QACvC,uCACK,sBAAsB,GACtB,qBAAqB,EACxB;IACJ,CAAC,EAAE,CAAC,sBAAsB,EAAE,qBAAqB,CAAC,CAAC,CAAC;IAEpD,MAAM,cAAc,GAAG,WAAW,CAChC,CAAC,MAAe,EAAE,OAA6B,EAAE,EAAE;QACjD,OAAO,CACL,oBAAC,KAAK,IAAC,SAAS,EAAE,WAAW,CAAC,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;YACpF,oBAAC,KAAK,IAAC,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,EAAE;gBAC5D,oBAAC,aAAa,kBAAC,MAAM,EAAE,MAAM,IAAM,OAAO,IAAE,YAAY,EAAE,KAAK,CAAC,wBAAwB,IAAI,CACtF,CACF,CACT,CAAC;IACJ,CAAC,EACD,CAAC,KAAK,CAAC,wBAAwB,CAAC,CACjC,CAAC;IAEF,yBAAyB,CAAC,CAAC,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC;IACnD,MAAM,oBAAoB,GAAG,OAAO,CAAC,GAAG,EAAE;QACxC,OAAO,CACL,oBAAC,YAAY,oBACP,iBAAiB,IACrB,qBAAqB,EAAE,qBAAqB,EAC5C,sBAAsB,EAAE,sBAAsB,EAC9C,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAC,oBAAoB,EAC3B,gCAAgC,EAAE,KAAK,CAAC,QAAQ,EAChD,gCAAgC,EAAE,mBAAmB,EACrD,cAAc,EAAE,cAAc,IAC9B,CACH,CAAC;IACJ,CAAC,EAAE,CAAC,iBAAiB,EAAE,KAAK,CAAC,QAAQ,EAAE,cAAc,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAE7E,OAAO,oBAAoB,CAAC;AAC9B,CAAC,CAAC;AAEF;;;;;GAKG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,CAAC,qBAA8B,EAAE,gBAA0B,EAAQ,EAAE;IAC5G,+FAA+F;IAC/F,qEAAqE;IACrE,sBAAsB;IACtB,mBAAmB;IACnB,4EAA4E;IAC5E,EAAE;IACF,2EAA2E;IAC3E,MAAM,CAAC,oBAAoB,EAAE,uBAAuB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxE,MAAM,iBAAiB,GAAG,WAAW,CAAC,oBAAoB,CAAC,CAAC;IAC5D,MAAM,oBAAoB,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC;IACvD,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,gBAAgB,KAAK,KAAK,EAAE;YAC9B,IAAI,iBAAiB,IAAI,CAAC,qBAAqB,IAAI,CAAC,oBAAoB,EAAE;gBACxE,oBAAoB,CAAC,iBAAiB,EAAE,CAAC;aAC1C;YACD,uBAAuB,CAAC,IAAI,CAAC,CAAC;SAC/B;IACH,CAAC,EAAE,CAAC,gBAAgB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,oBAAoB,CAAC,CAAC,CAAC;AAC/G,CAAC,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport React, { useCallback, useEffect, useMemo, useState } from 'react';\nimport {\n VideoGallery,\n VideoStreamOptions,\n OnRenderAvatarCallback,\n CustomAvatarOptions\n} from '@internal/react-components';\nimport { usePropsFor } from '../hooks/usePropsFor';\nimport { AvatarPersona, AvatarPersonaDataCallback } from '../../common/AvatarPersona';\nimport { mergeStyles, Stack } from '@fluentui/react';\nimport { getIsPreviewCameraOn } from '../selectors/baseSelectors';\nimport { useHandlers } from '../hooks/useHandlers';\nimport { useSelector } from '../hooks/useSelector';\nimport { localVideoCameraCycleButtonSelector } from '../selectors/LocalVideoTileSelector';\nimport { LocalVideoCameraCycleButton } from '@internal/react-components';\n\nconst VideoGalleryStyles = {\n root: {\n height: '100%',\n minHeight: '10rem', // space affordance to ensure media gallery is never collapsed\n minWidth: '6rem'\n }\n};\n\nconst localVideoViewOptions = {\n scalingMode: 'Crop',\n isMirrored: true\n} as VideoStreamOptions;\n\nconst remoteVideoViewOptions = {\n scalingMode: 'Crop'\n} as VideoStreamOptions;\n\n/**\n * @private\n */\nexport interface MediaGalleryProps {\n isVideoStreamOn?: boolean;\n isMicrophoneChecked?: boolean;\n onStartLocalVideo: () => Promise<void>;\n onRenderAvatar?: OnRenderAvatarCallback;\n onFetchAvatarPersonaData?: AvatarPersonaDataCallback;\n isMobile?: boolean;\n}\n\n/**\n * @private\n */\nexport const MediaGallery = (props: MediaGalleryProps): JSX.Element => {\n const videoGalleryProps = usePropsFor(VideoGallery);\n const cameraSwitcherCameras = useSelector(localVideoCameraCycleButtonSelector);\n const cameraSwitcherCallback = useHandlers(LocalVideoCameraCycleButton);\n const cameraSwitcherProps = useMemo(() => {\n return {\n ...cameraSwitcherCallback,\n ...cameraSwitcherCameras\n };\n }, [cameraSwitcherCallback, cameraSwitcherCameras]);\n\n const onRenderAvatar = useCallback(\n (userId?: string, options?: CustomAvatarOptions) => {\n return (\n <Stack className={mergeStyles({ position: 'absolute', height: '100%', width: '100%' })}>\n <Stack styles={{ root: { margin: 'auto', maxHeight: '100%' } }}>\n <AvatarPersona userId={userId} {...options} dataProvider={props.onFetchAvatarPersonaData} />\n </Stack>\n </Stack>\n );\n },\n [props.onFetchAvatarPersonaData]\n );\n\n useLocalVideoStartTrigger(!!props.isVideoStreamOn);\n const VideoGalleryMemoized = useMemo(() => {\n return (\n <VideoGallery\n {...videoGalleryProps}\n localVideoViewOptions={localVideoViewOptions}\n remoteVideoViewOptions={remoteVideoViewOptions}\n styles={VideoGalleryStyles}\n layout=\"floatingLocalVideo\"\n showCameraSwitcherInLocalPreview={props.isMobile}\n localVideoCameraCycleButtonProps={cameraSwitcherProps}\n onRenderAvatar={onRenderAvatar}\n />\n );\n }, [videoGalleryProps, props.isMobile, onRenderAvatar, cameraSwitcherProps]);\n\n return VideoGalleryMemoized;\n};\n\n/**\n * @private\n *\n * `shouldTransition` is an extra predicate that controls whether this hooks actually transitions the call.\n * The rule of hooks disallows calling the hook conditionally, so this predicate can be used to make the decision.\n */\nexport const useLocalVideoStartTrigger = (isLocalVideoAvailable: boolean, shouldTransition?: boolean): void => {\n // Once a call is joined, we need to transition the local preview camera setting into the call.\n // This logic is needed on any screen that we might join a call from:\n // - The Media gallery\n // - The lobby page\n // - The networkReconnect interstitial that may show at the start of a call.\n //\n // @TODO: Can we simply have the callHandlers handle this transition logic.\n const [isButtonStatusSynced, setIsButtonStatusSynced] = useState(false);\n const isPreviewCameraOn = useSelector(getIsPreviewCameraOn);\n const mediaGalleryHandlers = useHandlers(MediaGallery);\n useEffect(() => {\n if (shouldTransition !== false) {\n if (isPreviewCameraOn && !isLocalVideoAvailable && !isButtonStatusSynced) {\n mediaGalleryHandlers.onStartLocalVideo();\n }\n setIsButtonStatusSynced(true);\n }\n }, [shouldTransition, isButtonStatusSynced, isPreviewCameraOn, isLocalVideoAvailable, mediaGalleryHandlers]);\n};\n\"../../../../../react-components/src\""]}
1
+ {"version":3,"file":"MediaGallery.js","sourceRoot":"","sources":["../../../../../../../../react-composites/src/composites/CallComposite/components/MediaGallery.tsx"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport React, { useCallback, useEffect, useMemo, useState } from 'react';\nimport {\n VideoGallery,\n VideoStreamOptions,\n OnRenderAvatarCallback,\n CustomAvatarOptions,\n Announcer\n} from '@internal/react-components';\nimport { usePropsFor } from '../hooks/usePropsFor';\nimport { AvatarPersona, AvatarPersonaDataCallback } from '../../common/AvatarPersona';\nimport { mergeStyles, Stack } from '@fluentui/react';\nimport { getIsPreviewCameraOn } from '../selectors/baseSelectors';\nimport { useHandlers } from '../hooks/useHandlers';\nimport { useSelector } from '../hooks/useSelector';\nimport { localVideoCameraCycleButtonSelector } from '../selectors/LocalVideoTileSelector';\nimport { LocalVideoCameraCycleButton } from '@internal/react-components';\nimport { _formatString } from '@internal/acs-ui-common';\nimport { useParticipantChangedAnnouncement } from '../utils/MediaGalleryUtils';\n\nconst VideoGalleryStyles = {\n root: {\n height: '100%',\n minHeight: '10rem', // space affordance to ensure media gallery is never collapsed\n minWidth: '6rem'\n }\n};\n\nconst localVideoViewOptions = {\n scalingMode: 'Crop',\n isMirrored: true\n} as VideoStreamOptions;\n\nconst remoteVideoViewOptions = {\n scalingMode: 'Crop'\n} as VideoStreamOptions;\n\n/**\n * @private\n */\nexport interface MediaGalleryProps {\n isVideoStreamOn?: boolean;\n isMicrophoneChecked?: boolean;\n onStartLocalVideo: () => Promise<void>;\n onRenderAvatar?: OnRenderAvatarCallback;\n onFetchAvatarPersonaData?: AvatarPersonaDataCallback;\n isMobile?: boolean;\n}\n\n/**\n * @private\n */\nexport const MediaGallery = (props: MediaGalleryProps): JSX.Element => {\n const videoGalleryProps = usePropsFor(VideoGallery);\n const cameraSwitcherCameras = useSelector(localVideoCameraCycleButtonSelector);\n const cameraSwitcherCallback = useHandlers(LocalVideoCameraCycleButton);\n const announcerString = useParticipantChangedAnnouncement();\n\n const cameraSwitcherProps = useMemo(() => {\n return {\n ...cameraSwitcherCallback,\n ...cameraSwitcherCameras\n };\n }, [cameraSwitcherCallback, cameraSwitcherCameras]);\n\n const onRenderAvatar = useCallback(\n (userId?: string, options?: CustomAvatarOptions) => {\n return (\n <Stack className={mergeStyles({ position: 'absolute', height: '100%', width: '100%' })}>\n <Stack styles={{ root: { margin: 'auto', maxHeight: '100%' } }}>\n <AvatarPersona userId={userId} {...options} dataProvider={props.onFetchAvatarPersonaData} />\n </Stack>\n </Stack>\n );\n },\n [props.onFetchAvatarPersonaData]\n );\n\n useLocalVideoStartTrigger(!!props.isVideoStreamOn);\n const VideoGalleryMemoized = useMemo(() => {\n return (\n <VideoGallery\n {...videoGalleryProps}\n localVideoViewOptions={localVideoViewOptions}\n remoteVideoViewOptions={remoteVideoViewOptions}\n styles={VideoGalleryStyles}\n layout=\"floatingLocalVideo\"\n showCameraSwitcherInLocalPreview={props.isMobile}\n localVideoCameraCycleButtonProps={cameraSwitcherProps}\n onRenderAvatar={onRenderAvatar}\n />\n );\n }, [videoGalleryProps, props.isMobile, onRenderAvatar, cameraSwitcherProps]);\n\n return (\n <>\n <Announcer announcementString={announcerString} ariaLive={'polite'} />\n {VideoGalleryMemoized}\n </>\n );\n};\n\n/**\n * @private\n *\n * `shouldTransition` is an extra predicate that controls whether this hooks actually transitions the call.\n * The rule of hooks disallows calling the hook conditionally, so this predicate can be used to make the decision.\n */\nexport const useLocalVideoStartTrigger = (isLocalVideoAvailable: boolean, shouldTransition?: boolean): void => {\n // Once a call is joined, we need to transition the local preview camera setting into the call.\n // This logic is needed on any screen that we might join a call from:\n // - The Media gallery\n // - The lobby page\n // - The networkReconnect interstitial that may show at the start of a call.\n //\n // @TODO: Can we simply have the callHandlers handle this transition logic.\n const [isButtonStatusSynced, setIsButtonStatusSynced] = useState(false);\n const isPreviewCameraOn = useSelector(getIsPreviewCameraOn);\n const mediaGalleryHandlers = useHandlers(MediaGallery);\n useEffect(() => {\n if (shouldTransition !== false) {\n if (isPreviewCameraOn && !isLocalVideoAvailable && !isButtonStatusSynced) {\n mediaGalleryHandlers.onStartLocalVideo();\n }\n setIsButtonStatusSynced(true);\n }\n }, [shouldTransition, isButtonStatusSynced, isPreviewCameraOn, isLocalVideoAvailable, mediaGalleryHandlers]);\n};\n\"../../../../../react-components/src\"\"../../../../../acs-ui-common/src\""]}
@@ -7,4 +7,11 @@ export declare const mediaGallerySelector: reselect.OutputSelector<import("../..
7
7
  }, (res: import("../../../../../communication-react/src/index").LocalVideoStreamState[] | undefined) => {
8
8
  isVideoStreamOn: boolean;
9
9
  }>;
10
+ /**
11
+ * Custom selector for this hook to retrieve all the participants that are currently
12
+ * connected to the call.
13
+ */
14
+ export declare const getRemoteParticipantsConnectedSelector: reselect.OutputSelector<import("../../../../../communication-react/src/index").CallAdapterState, import("../../../../../communication-react/src/index").RemoteParticipantState[], (res: {
15
+ [keys: string]: import("../../../../../communication-react/src/index").RemoteParticipantState;
16
+ } | undefined) => import("../../../../../communication-react/src/index").RemoteParticipantState[]>;
10
17
  //# sourceMappingURL=mediaGallerySelector.d.ts.map
@@ -1,7 +1,7 @@
1
1
  // Copyright (c) Microsoft Corporation.
2
2
  // Licensed under the MIT license.
3
3
  import * as reselect from 'reselect';
4
- import { getLocalVideoStreams } from './baseSelectors';
4
+ import { getLocalVideoStreams, getRemoteParticipants } from './baseSelectors';
5
5
  /**
6
6
  * @private
7
7
  */
@@ -11,4 +11,12 @@ export const mediaGallerySelector = reselect.createSelector([getLocalVideoStream
11
11
  isVideoStreamOn: !!((_b = (_a = localVideoStreams === null || localVideoStreams === void 0 ? void 0 : localVideoStreams.find((stream) => stream.mediaStreamType === 'Video')) === null || _a === void 0 ? void 0 : _a.view) === null || _b === void 0 ? void 0 : _b.target)
12
12
  };
13
13
  });
14
+ /**
15
+ * Custom selector for this hook to retrieve all the participants that are currently
16
+ * connected to the call.
17
+ */
18
+ export const getRemoteParticipantsConnectedSelector = reselect.createSelector([getRemoteParticipants], (remoteParticipants) => {
19
+ const participants = Object.values(remoteParticipants !== null && remoteParticipants !== void 0 ? remoteParticipants : {});
20
+ return participants.filter((p) => p.state === 'Connected');
21
+ });
14
22
  //# sourceMappingURL=mediaGallerySelector.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mediaGallerySelector.js","sourceRoot":"","sources":["../../../../../../../../react-composites/src/composites/CallComposite/selectors/mediaGallerySelector.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAEvD;;GAEG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAC,oBAAoB,CAAC,EAAE,CAAC,iBAAiB,EAAE,EAAE;;IACxG,OAAO;QACL,eAAe,EAAE,CAAC,CAAC,CAAA,MAAA,MAAA,iBAAiB,aAAjB,iBAAiB,uBAAjB,iBAAiB,CAAE,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,eAAe,KAAK,OAAO,CAAC,0CAAE,IAAI,0CAAE,MAAM,CAAA;KACzG,CAAC;AACJ,CAAC,CAAC,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport * as reselect from 'reselect';\nimport { getLocalVideoStreams } from './baseSelectors';\n\n/**\n * @private\n */\nexport const mediaGallerySelector = reselect.createSelector([getLocalVideoStreams], (localVideoStreams) => {\n return {\n isVideoStreamOn: !!localVideoStreams?.find((stream) => stream.mediaStreamType === 'Video')?.view?.target\n };\n});\n"]}
1
+ {"version":3,"file":"mediaGallerySelector.js","sourceRoot":"","sources":["../../../../../../../../react-composites/src/composites/CallComposite/selectors/mediaGallerySelector.ts"],"names":[],"mappings":"AAAA,uCAAuC;AACvC,kCAAkC;AAElC,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAE9E;;GAEG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAC,oBAAoB,CAAC,EAAE,CAAC,iBAAiB,EAAE,EAAE;;IACxG,OAAO;QACL,eAAe,EAAE,CAAC,CAAC,CAAA,MAAA,MAAA,iBAAiB,aAAjB,iBAAiB,uBAAjB,iBAAiB,CAAE,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,eAAe,KAAK,OAAO,CAAC,0CAAE,IAAI,0CAAE,MAAM,CAAA;KACzG,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,sCAAsC,GAAG,QAAQ,CAAC,cAAc,CAC3E,CAAC,qBAAqB,CAAC,EACvB,CAAC,kBAAkB,EAAE,EAAE;IACrB,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,CAAC,kBAAkB,aAAlB,kBAAkB,cAAlB,kBAAkB,GAAI,EAAE,CAAC,CAAC;IAC7D,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC;AAC7D,CAAC,CACF,CAAC","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport * as reselect from 'reselect';\nimport { getLocalVideoStreams, getRemoteParticipants } from './baseSelectors';\n\n/**\n * @private\n */\nexport const mediaGallerySelector = reselect.createSelector([getLocalVideoStreams], (localVideoStreams) => {\n return {\n isVideoStreamOn: !!localVideoStreams?.find((stream) => stream.mediaStreamType === 'Video')?.view?.target\n };\n});\n\n/**\n * Custom selector for this hook to retrieve all the participants that are currently\n * connected to the call.\n */\nexport const getRemoteParticipantsConnectedSelector = reselect.createSelector(\n [getRemoteParticipants],\n (remoteParticipants) => {\n const participants = Object.values(remoteParticipants ?? {});\n return participants.filter((p) => p.state === 'Connected');\n }\n);\n"]}
@@ -0,0 +1,29 @@
1
+ import { RemoteParticipantState } from "../../../../../calling-stateful-client/src";
2
+ declare type ParticipantChangedAnnouncmentStrings = {
3
+ participantJoinedNoticeString: string;
4
+ twoParticipantJoinedNoticeString: string;
5
+ threeParticipantJoinedNoticeString: string;
6
+ participantLeftNoticeString: string;
7
+ twoParticipantLeftNoticeString: string;
8
+ threeParticipantLeftNoticeString: string;
9
+ unnamedParticipantString: string;
10
+ manyParticipantsJoined: string;
11
+ manyParticipantsLeft: string;
12
+ manyUnnamedParticipantsJoined: string;
13
+ manyUnnamedParticipantsLeft: string;
14
+ };
15
+ /**
16
+ * sets the announcement string whenever a Participant comes or goes from a call to be
17
+ * used by the system narrator.
18
+ *
19
+ * @returns string to be used by the narrator and Announcer component
20
+ *
21
+ * @internal
22
+ */
23
+ export declare const useParticipantChangedAnnouncement: () => string;
24
+ /**
25
+ * Generates the announcement string for when a participant joins or leaves a call.
26
+ */
27
+ export declare const createAnnouncmentString: (direction: 'joined' | 'left', participants: RemoteParticipantState[], strings: ParticipantChangedAnnouncmentStrings) => string;
28
+ export {};
29
+ //# sourceMappingURL=MediaGalleryUtils.d.ts.map
@@ -0,0 +1,119 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT license.
3
+ import { toFlatCommunicationIdentifier, _formatString } from "../../../../../acs-ui-common/src";
4
+ import { useMemo, useRef, useState } from 'react';
5
+ import { useLocale } from '../../localization';
6
+ import { useSelector } from '../hooks/useSelector';
7
+ import { getRemoteParticipantsConnectedSelector } from '../selectors/mediaGallerySelector';
8
+ /**
9
+ * sets the announcement string whenever a Participant comes or goes from a call to be
10
+ * used by the system narrator.
11
+ *
12
+ * @returns string to be used by the narrator and Announcer component
13
+ *
14
+ * @internal
15
+ */
16
+ export const useParticipantChangedAnnouncement = () => {
17
+ const locale = useLocale().strings.call;
18
+ const strings = useMemo(() => {
19
+ return {
20
+ participantJoinedNoticeString: locale.participantJoinedNoticeString,
21
+ twoParticipantJoinedNoticeString: locale.twoParticipantJoinedNoticeString,
22
+ threeParticipantJoinedNoticeString: locale.threeParticipantJoinedNoticeString,
23
+ participantLeftNoticeString: locale.participantLeftNoticeString,
24
+ twoParticipantLeftNoticeString: locale.twoParticipantLeftNoticeString,
25
+ threeParticipantLeftNoticeString: locale.threeParticipantLeftNoticeString,
26
+ unnamedParticipantString: locale.unnamedParticipantString,
27
+ manyParticipantsJoined: locale.manyParticipantsJoined,
28
+ manyParticipantsLeft: locale.manyParticipantsLeft,
29
+ manyUnnamedParticipantsJoined: locale.manyUnnamedParticipantsJoined,
30
+ manyUnnamedParticipantsLeft: locale.manyUnnamedParticipantsLeft
31
+ };
32
+ }, [locale]);
33
+ const [announcerString, setAnnouncerString] = useState('');
34
+ const currentParticipants = useSelector(getRemoteParticipantsConnectedSelector);
35
+ /**
36
+ * We want to use a useRef here since we want to not fire this hook based on the previous participants
37
+ * this allows this value to be used in the hook without being in the dependency array.
38
+ *
39
+ * Note: By definition if this hook is used in another component it is not pure anymore.
40
+ */
41
+ const previousParticipants = useRef(currentParticipants);
42
+ const resetAnnoucement = (string) => {
43
+ setAnnouncerString(string);
44
+ };
45
+ useMemo(() => {
46
+ const currentIds = currentParticipants.map((p) => toFlatCommunicationIdentifier(p.identifier));
47
+ const previousIds = previousParticipants.current.map((p) => toFlatCommunicationIdentifier(p.identifier));
48
+ const whoJoined = currentParticipants.filter((p) => !previousIds.includes(toFlatCommunicationIdentifier(p.identifier)));
49
+ const whoLeft = previousParticipants.current.filter((p) => !currentIds.includes(toFlatCommunicationIdentifier(p.identifier)));
50
+ if (whoJoined.length > 0) {
51
+ resetAnnoucement(createAnnouncmentString('joined', whoJoined, strings));
52
+ }
53
+ if (whoLeft.length > 0) {
54
+ resetAnnoucement(createAnnouncmentString('left', whoLeft, strings));
55
+ }
56
+ // Update cached value at the end.
57
+ previousParticipants.current = currentParticipants;
58
+ }, [currentParticipants, strings]);
59
+ return announcerString;
60
+ };
61
+ /**
62
+ * Generates the announcement string for when a participant joins or leaves a call.
63
+ */
64
+ export const createAnnouncmentString = (direction, participants, strings) => {
65
+ var _a, _b, _c;
66
+ /**
67
+ * If there are no participants return empty string.
68
+ */
69
+ if (participants.length === 0) {
70
+ return '';
71
+ }
72
+ /**
73
+ * Filter participants into two arrays to put all the unnamed participants at the back of the
74
+ * names being announced.
75
+ */
76
+ const unnamedParticipants = participants.filter((p) => p.displayName === undefined);
77
+ const namedParicipants = participants.filter((p) => p.displayName);
78
+ const sortedParticipants = namedParicipants.concat(unnamedParticipants);
79
+ /**
80
+ * if there are only unnamed participants present in the array announce a special unnamed participants
81
+ * only string.
82
+ */
83
+ if (sortedParticipants.filter((p) => p.displayName).length === 0 && sortedParticipants.length > 1) {
84
+ return _formatString(direction === 'joined' ? strings.manyUnnamedParticipantsJoined : strings.manyUnnamedParticipantsLeft, {
85
+ numOfParticipants: (sortedParticipants.length - 1).toString()
86
+ });
87
+ }
88
+ const participantNames = sortedParticipants.map((p) => { var _a; return (_a = p.displayName) !== null && _a !== void 0 ? _a : strings.unnamedParticipantString; });
89
+ switch (sortedParticipants.length) {
90
+ case 1:
91
+ return _formatString(direction === 'joined' ? strings.participantJoinedNoticeString : strings.participantLeftNoticeString, { displayName: participantNames[0] });
92
+ case 2:
93
+ return _formatString(direction === 'joined' ? strings.twoParticipantJoinedNoticeString : strings.twoParticipantLeftNoticeString, {
94
+ displayName1: participantNames[0],
95
+ displayName2: participantNames[1]
96
+ });
97
+ case 3:
98
+ return _formatString(direction === 'joined' ? strings.threeParticipantJoinedNoticeString : strings.threeParticipantLeftNoticeString, {
99
+ displayName1: participantNames[0],
100
+ displayName2: participantNames[1],
101
+ displayName3: participantNames[2]
102
+ });
103
+ }
104
+ /**
105
+ * If we have more than 3 participants joining we need to do something more to announce them
106
+ * appropriately.
107
+ *
108
+ * We don't want to announce every name when more than 3 participants join at once so
109
+ * we parse out the first 3 names we have and announce those with the number of others.
110
+ */
111
+ const numberOfExtraParticipants = sortedParticipants.length - 3;
112
+ return _formatString(direction === 'joined' ? strings.manyParticipantsJoined : strings.manyParticipantsLeft, {
113
+ displayName1: (_a = sortedParticipants[0].displayName) !== null && _a !== void 0 ? _a : strings.unnamedParticipantString,
114
+ displayName2: (_b = sortedParticipants[1].displayName) !== null && _b !== void 0 ? _b : strings.unnamedParticipantString,
115
+ displayName3: (_c = sortedParticipants[2].displayName) !== null && _c !== void 0 ? _c : strings.unnamedParticipantString,
116
+ numOfParticipants: numberOfExtraParticipants.toString()
117
+ });
118
+ };
119
+ //# sourceMappingURL=MediaGalleryUtils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MediaGalleryUtils.js","sourceRoot":"","sources":["../../../../../../../../react-composites/src/composites/CallComposite/utils/MediaGalleryUtils.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT license.\n\nimport { toFlatCommunicationIdentifier, _formatString } from '@internal/acs-ui-common';\nimport { RemoteParticipantState } from '@internal/calling-stateful-client';\nimport { useMemo, useRef, useState } from 'react';\nimport { useLocale } from '../../localization';\nimport { useSelector } from '../hooks/useSelector';\nimport { getRemoteParticipantsConnectedSelector } from '../selectors/mediaGallerySelector';\n\ntype ParticipantChangedAnnouncmentStrings = {\n participantJoinedNoticeString: string;\n twoParticipantJoinedNoticeString: string;\n threeParticipantJoinedNoticeString: string;\n participantLeftNoticeString: string;\n twoParticipantLeftNoticeString: string;\n threeParticipantLeftNoticeString: string;\n unnamedParticipantString: string;\n manyParticipantsJoined: string;\n manyParticipantsLeft: string;\n manyUnnamedParticipantsJoined: string;\n manyUnnamedParticipantsLeft: string;\n};\n\n/**\n * sets the announcement string whenever a Participant comes or goes from a call to be\n * used by the system narrator.\n *\n * @returns string to be used by the narrator and Announcer component\n *\n * @internal\n */\nexport const useParticipantChangedAnnouncement = (): string => {\n const locale = useLocale().strings.call;\n const strings = useMemo(() => {\n return {\n participantJoinedNoticeString: locale.participantJoinedNoticeString,\n twoParticipantJoinedNoticeString: locale.twoParticipantJoinedNoticeString,\n threeParticipantJoinedNoticeString: locale.threeParticipantJoinedNoticeString,\n participantLeftNoticeString: locale.participantLeftNoticeString,\n twoParticipantLeftNoticeString: locale.twoParticipantLeftNoticeString,\n threeParticipantLeftNoticeString: locale.threeParticipantLeftNoticeString,\n unnamedParticipantString: locale.unnamedParticipantString,\n manyParticipantsJoined: locale.manyParticipantsJoined,\n manyParticipantsLeft: locale.manyParticipantsLeft,\n manyUnnamedParticipantsJoined: locale.manyUnnamedParticipantsJoined,\n manyUnnamedParticipantsLeft: locale.manyUnnamedParticipantsLeft\n };\n }, [locale]);\n const [announcerString, setAnnouncerString] = useState<string>('');\n const currentParticipants = useSelector(getRemoteParticipantsConnectedSelector);\n /**\n * We want to use a useRef here since we want to not fire this hook based on the previous participants\n * this allows this value to be used in the hook without being in the dependency array.\n *\n * Note: By definition if this hook is used in another component it is not pure anymore.\n */\n const previousParticipants = useRef<RemoteParticipantState[]>(currentParticipants);\n\n const resetAnnoucement = (string: string): void => {\n setAnnouncerString(string);\n };\n\n useMemo(() => {\n const currentIds = currentParticipants.map((p) => toFlatCommunicationIdentifier(p.identifier));\n const previousIds = previousParticipants.current.map((p) => toFlatCommunicationIdentifier(p.identifier));\n const whoJoined = currentParticipants.filter(\n (p) => !previousIds.includes(toFlatCommunicationIdentifier(p.identifier))\n );\n const whoLeft = previousParticipants.current.filter(\n (p) => !currentIds.includes(toFlatCommunicationIdentifier(p.identifier))\n );\n if (whoJoined.length > 0) {\n resetAnnoucement(createAnnouncmentString('joined', whoJoined, strings));\n }\n if (whoLeft.length > 0) {\n resetAnnoucement(createAnnouncmentString('left', whoLeft, strings));\n }\n // Update cached value at the end.\n previousParticipants.current = currentParticipants;\n }, [currentParticipants, strings]);\n return announcerString;\n};\n\n/**\n * Generates the announcement string for when a participant joins or leaves a call.\n */\nexport const createAnnouncmentString = (\n direction: 'joined' | 'left',\n participants: RemoteParticipantState[],\n strings: ParticipantChangedAnnouncmentStrings\n): string => {\n /**\n * If there are no participants return empty string.\n */\n if (participants.length === 0) {\n return '';\n }\n /**\n * Filter participants into two arrays to put all the unnamed participants at the back of the\n * names being announced.\n */\n const unnamedParticipants = participants.filter((p) => p.displayName === undefined);\n const namedParicipants = participants.filter((p) => p.displayName);\n const sortedParticipants = namedParicipants.concat(unnamedParticipants);\n\n /**\n * if there are only unnamed participants present in the array announce a special unnamed participants\n * only string.\n */\n if (sortedParticipants.filter((p) => p.displayName).length === 0 && sortedParticipants.length > 1) {\n return _formatString(\n direction === 'joined' ? strings.manyUnnamedParticipantsJoined : strings.manyUnnamedParticipantsLeft,\n {\n numOfParticipants: (sortedParticipants.length - 1).toString()\n }\n );\n }\n const participantNames = sortedParticipants.map((p) => p.displayName ?? strings.unnamedParticipantString);\n\n switch (sortedParticipants.length) {\n case 1:\n return _formatString(\n direction === 'joined' ? strings.participantJoinedNoticeString : strings.participantLeftNoticeString,\n { displayName: participantNames[0] }\n );\n case 2:\n return _formatString(\n direction === 'joined' ? strings.twoParticipantJoinedNoticeString : strings.twoParticipantLeftNoticeString,\n {\n displayName1: participantNames[0],\n displayName2: participantNames[1]\n }\n );\n case 3:\n return _formatString(\n direction === 'joined' ? strings.threeParticipantJoinedNoticeString : strings.threeParticipantLeftNoticeString,\n {\n displayName1: participantNames[0],\n displayName2: participantNames[1],\n displayName3: participantNames[2]\n }\n );\n }\n\n /**\n * If we have more than 3 participants joining we need to do something more to announce them\n * appropriately.\n *\n * We don't want to announce every name when more than 3 participants join at once so\n * we parse out the first 3 names we have and announce those with the number of others.\n */\n\n const numberOfExtraParticipants = sortedParticipants.length - 3;\n\n return _formatString(direction === 'joined' ? strings.manyParticipantsJoined : strings.manyParticipantsLeft, {\n displayName1: sortedParticipants[0].displayName ?? strings.unnamedParticipantString,\n displayName2: sortedParticipants[1].displayName ?? strings.unnamedParticipantString,\n displayName3: sortedParticipants[2].displayName ?? strings.unnamedParticipantString,\n numOfParticipants: numberOfExtraParticipants.toString()\n });\n};\n\"../../../../../acs-ui-common/src\"\"../../../../../calling-stateful-client/src\""]}
@@ -68,7 +68,18 @@
68
68
  "holdScreenLabel": "You're on hold",
69
69
  "openDtmfDialpadLabel": "Show dialpad",
70
70
  "dtmfDialpadPlaceholderText": "Enter number",
71
- "outboundCallingNoticeString": "Calling..."
71
+ "outboundCallingNoticeString": "Calling...",
72
+ "participantJoinedNoticeString": "{displayName} joined",
73
+ "twoParticipantJoinedNoticeString": "{displayName1} and {displayName2} have joined",
74
+ "threeParticipantJoinedNoticeString": "{displayName1}, {displayName2} and {displayName3} have joined",
75
+ "participantLeftNoticeString": "{displayName} left",
76
+ "twoParticipantLeftNoticeString": "{displayName1} and {displayName2} have left",
77
+ "threeParticipantLeftNoticeString": "{displayName1}, {displayName2} and {displayName3} have left",
78
+ "unnamedParticipantString": "unnamed participant",
79
+ "manyUnnamedParticipantsJoined": "unnamed participant and {numOfParticipants} other participants joined",
80
+ "manyUnnamedParticipantsLeft": "unnamed participant and {numOfParticipants} other participants left",
81
+ "manyParticipantsJoined": "{displayName1}, {displayName2}, {displayName3} and {numOfParticipants} other participants joined",
82
+ "manyParticipantsLeft": "{displayName1}, {displayName2}, {displayName3} and {numOfParticipants} other participants left"
72
83
  },
73
84
  "chat": {
74
85
  "chatListHeader": "In this chat",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@azure/communication-react",
3
- "version": "1.4.2-alpha-202211240014.0",
3
+ "version": "1.4.2-alpha-202211290015.0",
4
4
  "sideEffects": false,
5
5
  "description": "React library for building modern communication user experiences utilizing Azure Communication Services",
6
6
  "keywords": [
@@ -86,13 +86,13 @@
86
86
  "@azure/core-auth": "1.3.2",
87
87
  "@babel/cli": "~7.16.0",
88
88
  "@babel/core": "~7.16.0",
89
- "@internal/calling-component-bindings": "1.4.2-alpha-202211240014.0",
90
- "@internal/calling-stateful-client": "1.4.2-alpha-202211240014.0",
91
- "@internal/chat-component-bindings": "1.4.2-alpha-202211240014.0",
92
- "@internal/chat-stateful-client": "1.4.2-alpha-202211240014.0",
93
- "@internal/fake-backends": "1.4.2-alpha-202211240014.0",
94
- "@internal/react-components": "1.4.2-alpha-202211240014.0",
95
- "@internal/react-composites": "1.4.2-alpha-202211240014.0",
89
+ "@internal/calling-component-bindings": "1.4.2-alpha-202211290015.0",
90
+ "@internal/calling-stateful-client": "1.4.2-alpha-202211290015.0",
91
+ "@internal/chat-component-bindings": "1.4.2-alpha-202211290015.0",
92
+ "@internal/chat-stateful-client": "1.4.2-alpha-202211290015.0",
93
+ "@internal/fake-backends": "1.4.2-alpha-202211290015.0",
94
+ "@internal/react-components": "1.4.2-alpha-202211290015.0",
95
+ "@internal/react-composites": "1.4.2-alpha-202211290015.0",
96
96
  "@microsoft/api-documenter": "~7.12.11",
97
97
  "@microsoft/api-extractor": "~7.18.0",
98
98
  "@rollup/plugin-json": "~4.1.0",