diff --git a/apps/meteor/client/contexts/VideoConfContext.ts b/apps/meteor/client/contexts/VideoConfContext.ts deleted file mode 100644 index 49a9006a1a81..000000000000 --- a/apps/meteor/client/contexts/VideoConfContext.ts +++ /dev/null @@ -1,86 +0,0 @@ -import type { IRoom } from '@rocket.chat/core-typings'; -import { createContext, useContext } from 'react'; -import { useSyncExternalStore } from 'use-sync-external-store/shim'; - -import type { DirectCallData, ProviderCapabilities, CallPreferences, VideoConfManager } from '../lib/VideoConfManager'; - -export type VideoConfPopupPayload = { - id: string; - rid: IRoom['_id']; - isReceiving?: boolean; -}; - -type VideoConfContextValue = { - manager: typeof VideoConfManager; - dispatchOutgoing: (options: Omit) => void; - dismissOutgoing: () => void; - startCall: (rid: IRoom['_id'], title?: string) => void; - acceptCall: (callId: string) => void; - joinCall: (callId: string) => void; - dismissCall: (callId: string) => void; - rejectIncomingCall: (callId: string) => void; - abortCall: () => void; - setPreferences: (prefs: { mic?: boolean; cam?: boolean }) => void; - queryIncomingCalls: { - subscribe: (cb: () => void) => () => void; - getSnapshot: () => DirectCallData[]; - }; - queryRinging: { - subscribe: (cb: () => void) => () => void; - getSnapshot: () => boolean; - }; - queryCalling: { - subscribe: (cb: () => void) => () => void; - getSnapshot: () => boolean; - }; - queryCapabilities: { - subscribe: (cb: () => void) => () => void; - getSnapshot: () => ProviderCapabilities; - }; - queryPreferences: { - subscribe: (cb: () => void) => () => void; - getSnapshot: () => CallPreferences; - }; -}; - -export const VideoConfContext = createContext(undefined); -const useVideoConfContext = (): VideoConfContextValue => { - const context = useContext(VideoConfContext); - if (!context) { - throw new Error('Must be running in VideoConf Context'); - } - - return context; -}; - -export const useVideoConfDispatchOutgoing = (): VideoConfContextValue['dispatchOutgoing'] => useVideoConfContext().dispatchOutgoing; -export const useVideoConfDismissOutgoing = (): VideoConfContextValue['dismissOutgoing'] => useVideoConfContext().dismissOutgoing; -export const useVideoConfStartCall = (): VideoConfContextValue['startCall'] => useVideoConfContext().startCall; -export const useVideoConfAcceptCall = (): VideoConfContextValue['acceptCall'] => useVideoConfContext().acceptCall; -export const useVideoConfJoinCall = (): VideoConfContextValue['joinCall'] => useVideoConfContext().joinCall; -export const useVideoConfDismissCall = (): VideoConfContextValue['dismissCall'] => useVideoConfContext().dismissCall; -export const useVideoConfAbortCall = (): VideoConfContextValue['abortCall'] => useVideoConfContext().abortCall; -export const useVideoConfRejectIncomingCall = (): VideoConfContextValue['rejectIncomingCall'] => useVideoConfContext().rejectIncomingCall; -export const useVideoConfIncomingCalls = (): DirectCallData[] => { - const { queryIncomingCalls } = useVideoConfContext(); - return useSyncExternalStore(queryIncomingCalls.subscribe, queryIncomingCalls.getSnapshot); -}; -export const useVideoConfSetPreferences = (): VideoConfContextValue['setPreferences'] => useVideoConfContext().setPreferences; -export const useVideoConfIsRinging = (): boolean => { - const { queryRinging } = useVideoConfContext(); - return useSyncExternalStore(queryRinging.subscribe, queryRinging.getSnapshot); -}; -export const useVideoConfIsCalling = (): boolean => { - const { queryCalling } = useVideoConfContext(); - return useSyncExternalStore(queryCalling.subscribe, queryCalling.getSnapshot); -}; -export const useVideoConfCapabilities = (): ProviderCapabilities => { - const { queryCapabilities } = useVideoConfContext(); - return useSyncExternalStore(queryCapabilities.subscribe, queryCapabilities.getSnapshot); -}; -export const useVideoConfPreferences = (): CallPreferences => { - const { queryPreferences } = useVideoConfContext(); - return useSyncExternalStore(queryPreferences.subscribe, queryPreferences.getSnapshot); -}; - -export const useVideoConfManager = (): typeof VideoConfManager | undefined => useContext(VideoConfContext)?.manager; diff --git a/apps/meteor/client/hooks/roomActions/useStartCallRoomAction/useVideoConfMenuOptions.tsx b/apps/meteor/client/hooks/roomActions/useStartCallRoomAction/useVideoConfMenuOptions.tsx index 89229a0e7510..aeae81dd8efe 100644 --- a/apps/meteor/client/hooks/roomActions/useStartCallRoomAction/useVideoConfMenuOptions.tsx +++ b/apps/meteor/client/hooks/roomActions/useStartCallRoomAction/useVideoConfMenuOptions.tsx @@ -3,10 +3,10 @@ import { Box } from '@rocket.chat/fuselage'; import { useEffectEvent, useStableArray } from '@rocket.chat/fuselage-hooks'; import type { GenericMenuItemProps } from '@rocket.chat/ui-client'; import { usePermission, useSetting, useUser } from '@rocket.chat/ui-contexts'; +import { useVideoConfDispatchOutgoing, useVideoConfIsCalling, useVideoConfIsRinging } from '@rocket.chat/ui-video-conf'; import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; -import { useVideoConfDispatchOutgoing, useVideoConfIsCalling, useVideoConfIsRinging } from '../../../contexts/VideoConfContext'; import { VideoConfManager } from '../../../lib/VideoConfManager'; import { useRoom } from '../../../views/room/contexts/RoomContext'; import type { RoomToolboxActionConfig } from '../../../views/room/contexts/RoomToolboxContext'; diff --git a/apps/meteor/client/lib/VideoConfManager.ts b/apps/meteor/client/lib/VideoConfManager.ts index 2fa07eba885f..6cb0e67a5999 100644 --- a/apps/meteor/client/lib/VideoConfManager.ts +++ b/apps/meteor/client/lib/VideoConfManager.ts @@ -1,4 +1,4 @@ -import type { IRoom, IUser } from '@rocket.chat/core-typings'; +import type { CallPreferences, DirectCallData, DirectCallParams, IRoom, IUser, ProviderCapabilities } from '@rocket.chat/core-typings'; import { Emitter } from '@rocket.chat/emitter'; import { Meteor } from 'meteor/meteor'; import { Tracker } from 'meteor/tracker'; @@ -17,32 +17,11 @@ const CALL_TIMEOUT = 10000; // How long are we gonna wait for a link after accepting an incoming call const ACCEPT_TIMEOUT = 5000; -type DirectCallParams = { - uid: IUser['_id']; - rid: IRoom['_id']; - callId: string; -}; - -export type DirectCallData = DirectCallParams & { - dismissed: boolean; -}; - type IncomingDirectCall = DirectCallParams & { timeout: ReturnType | undefined; acceptTimeout?: ReturnType | undefined; }; -export type CallPreferences = { - mic?: boolean; - cam?: boolean; -}; - -export type ProviderCapabilities = { - mic?: boolean; - cam?: boolean; - title?: boolean; -}; - type CurrentCallParams = { callId: string; url: string; diff --git a/apps/meteor/client/providers/VideoConfProvider.tsx b/apps/meteor/client/providers/VideoConfProvider.tsx index 42a5dc0c4623..81cc1bb4f780 100644 --- a/apps/meteor/client/providers/VideoConfProvider.tsx +++ b/apps/meteor/client/providers/VideoConfProvider.tsx @@ -1,12 +1,11 @@ -import type { IRoom } from '@rocket.chat/core-typings'; +import type { CallPreferences, DirectCallData, IRoom, ProviderCapabilities } from '@rocket.chat/core-typings'; import { useToastMessageDispatch, useSetting } from '@rocket.chat/ui-contexts'; +import type { VideoConfPopupPayload } from '@rocket.chat/ui-video-conf'; +import { VideoConfContext } from '@rocket.chat/ui-video-conf'; import type { ReactElement, ReactNode } from 'react'; import { useState, useMemo, useEffect } from 'react'; import { useTranslation } from 'react-i18next'; -import type { VideoConfPopupPayload } from '../contexts/VideoConfContext'; -import { VideoConfContext } from '../contexts/VideoConfContext'; -import type { DirectCallData, ProviderCapabilities, CallPreferences } from '../lib/VideoConfManager'; import { VideoConfManager } from '../lib/VideoConfManager'; import VideoConfPopups from '../views/room/contextualBar/VideoConference/VideoConfPopups'; import { useVideoConfOpenCall } from '../views/room/contextualBar/VideoConference/hooks/useVideoConfOpenCall'; @@ -44,7 +43,6 @@ const VideoConfContextProvider = ({ children }: { children: ReactNode }): ReactE const contextValue = useMemo( () => ({ - manager: VideoConfManager, dispatchOutgoing: (option: Omit): void => setOutgoing({ ...option, id: option.rid }), dismissOutgoing: (): void => setOutgoing(undefined), startCall: (rid: IRoom['_id'], confTitle?: string): Promise => VideoConfManager.startCall(rid, confTitle), @@ -56,6 +54,7 @@ const VideoConfContextProvider = ({ children }: { children: ReactNode }): ReactE rejectIncomingCall: (callId: string): void => VideoConfManager.rejectIncomingCall(callId), abortCall: (): void => VideoConfManager.abortCall(), setPreferences: (prefs: Partial<(typeof VideoConfManager)['preferences']>): void => VideoConfManager.setPreferences(prefs), + loadCapabilities: VideoConfManager.loadCapabilities, queryIncomingCalls: { getSnapshot: (): DirectCallData[] => VideoConfManager.getIncomingDirectCalls(), subscribe: (cb: () => void) => VideoConfManager.on('incoming/changed', cb), diff --git a/apps/meteor/client/sidebar/RoomList/RoomListRow.tsx b/apps/meteor/client/sidebar/RoomList/RoomListRow.tsx index 531c6a51c377..f7d511af4908 100644 --- a/apps/meteor/client/sidebar/RoomList/RoomListRow.tsx +++ b/apps/meteor/client/sidebar/RoomList/RoomListRow.tsx @@ -1,11 +1,11 @@ import type { IRoom, ISubscription } from '@rocket.chat/core-typings'; import { SidebarSection } from '@rocket.chat/fuselage'; +import { useVideoConfAcceptCall, useVideoConfRejectIncomingCall, useVideoConfIncomingCalls } from '@rocket.chat/ui-video-conf'; import type { TFunction } from 'i18next'; import type { ReactElement } from 'react'; import { memo, useMemo } from 'react'; import SideBarItemTemplateWithData from './SideBarItemTemplateWithData'; -import { useVideoConfAcceptCall, useVideoConfRejectIncomingCall, useVideoConfIncomingCalls } from '../../contexts/VideoConfContext'; import type { useAvatarTemplate } from '../hooks/useAvatarTemplate'; import type { useTemplateByViewMode } from '../hooks/useTemplateByViewMode'; diff --git a/apps/meteor/client/sidebar/hooks/useRoomList.ts b/apps/meteor/client/sidebar/hooks/useRoomList.ts index 1d387d90efe4..1bdb4a758c92 100644 --- a/apps/meteor/client/sidebar/hooks/useRoomList.ts +++ b/apps/meteor/client/sidebar/hooks/useRoomList.ts @@ -1,10 +1,10 @@ import type { ILivechatInquiryRecord, IRoom, ISubscription } from '@rocket.chat/core-typings'; import { useDebouncedState } from '@rocket.chat/fuselage-hooks'; import { useUserPreference, useUserSubscriptions, useSetting } from '@rocket.chat/ui-contexts'; +import { useVideoConfIncomingCalls } from '@rocket.chat/ui-video-conf'; import { useEffect } from 'react'; import { useQueryOptions } from './useQueryOptions'; -import { useVideoConfIncomingCalls } from '../../contexts/VideoConfContext'; import { useOmnichannelEnabled } from '../../hooks/omnichannel/useOmnichannelEnabled'; import { useQueuedInquiries } from '../../hooks/omnichannel/useQueuedInquiries'; diff --git a/apps/meteor/client/sidebarv2/RoomList/RoomListRow.tsx b/apps/meteor/client/sidebarv2/RoomList/RoomListRow.tsx index 64836a353d8a..832699a092ec 100644 --- a/apps/meteor/client/sidebarv2/RoomList/RoomListRow.tsx +++ b/apps/meteor/client/sidebarv2/RoomList/RoomListRow.tsx @@ -1,9 +1,9 @@ import type { SubscriptionWithRoom } from '@rocket.chat/ui-contexts'; +import { useVideoConfAcceptCall, useVideoConfRejectIncomingCall, useVideoConfIncomingCalls } from '@rocket.chat/ui-video-conf'; import type { TFunction } from 'i18next'; import { memo, useMemo } from 'react'; import SidebarItemTemplateWithData from './SidebarItemTemplateWithData'; -import { useVideoConfAcceptCall, useVideoConfRejectIncomingCall, useVideoConfIncomingCalls } from '../../contexts/VideoConfContext'; import type { useAvatarTemplate } from '../hooks/useAvatarTemplate'; import type { useTemplateByViewMode } from '../hooks/useTemplateByViewMode'; diff --git a/apps/meteor/client/sidebarv2/hooks/useRoomList.spec.tsx b/apps/meteor/client/sidebarv2/hooks/useRoomList.spec.tsx index a7d1da366095..ac04d1a9e0c2 100644 --- a/apps/meteor/client/sidebarv2/hooks/useRoomList.spec.tsx +++ b/apps/meteor/client/sidebarv2/hooks/useRoomList.spec.tsx @@ -1,10 +1,10 @@ import { mockAppRoot } from '@rocket.chat/mock-providers'; import type { SubscriptionWithRoom } from '@rocket.chat/ui-contexts'; +import { VideoConfContext } from '@rocket.chat/ui-video-conf'; import { renderHook } from '@testing-library/react'; import { useRoomList } from './useRoomList'; import { createFakeRoom, createFakeSubscription, createFakeUser } from '../../../tests/mocks/data'; -import { VideoConfContext } from '../../contexts/VideoConfContext'; const user = createFakeUser({ active: true, diff --git a/apps/meteor/client/sidebarv2/hooks/useRoomList.ts b/apps/meteor/client/sidebarv2/hooks/useRoomList.ts index bebaf962ecf3..40ac4db1c71b 100644 --- a/apps/meteor/client/sidebarv2/hooks/useRoomList.ts +++ b/apps/meteor/client/sidebarv2/hooks/useRoomList.ts @@ -2,9 +2,9 @@ import type { ILivechatInquiryRecord } from '@rocket.chat/core-typings'; import { useDebouncedValue } from '@rocket.chat/fuselage-hooks'; import type { SubscriptionWithRoom, TranslationKey } from '@rocket.chat/ui-contexts'; import { useUserPreference, useUserSubscriptions, useSetting } from '@rocket.chat/ui-contexts'; +import { useVideoConfIncomingCalls } from '@rocket.chat/ui-video-conf'; import { useMemo } from 'react'; -import { useVideoConfIncomingCalls } from '../../contexts/VideoConfContext'; import { useOmnichannelEnabled } from '../../hooks/omnichannel/useOmnichannelEnabled'; import { useQueuedInquiries } from '../../hooks/omnichannel/useQueuedInquiries'; import { useSortQueryOptions } from '../../hooks/useSortQueryOptions'; diff --git a/apps/meteor/client/uikit/hooks/useMessageBlockContextValue.ts b/apps/meteor/client/uikit/hooks/useMessageBlockContextValue.ts index b99b7eb0f1b4..7522e14c600d 100644 --- a/apps/meteor/client/uikit/hooks/useMessageBlockContextValue.ts +++ b/apps/meteor/client/uikit/hooks/useMessageBlockContextValue.ts @@ -1,17 +1,17 @@ import type { IRoom, IMessage } from '@rocket.chat/core-typings'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import type { UiKitContext } from '@rocket.chat/fuselage-ui-kit'; -import type { ContextType } from 'react'; - -import { useUiKitActionManager } from './useUiKitActionManager'; import { useVideoConfDispatchOutgoing, useVideoConfIsCalling, useVideoConfIsRinging, useVideoConfJoinCall, - useVideoConfManager, + useVideoConfLoadCapabilities, useVideoConfSetPreferences, -} from '../../contexts/VideoConfContext'; +} from '@rocket.chat/ui-video-conf'; +import type { ContextType } from 'react'; + +import { useUiKitActionManager } from './useUiKitActionManager'; import { useVideoConfWarning } from '../../views/room/contextualBar/VideoConference/hooks/useVideoConfWarning'; export const useMessageBlockContextValue = (rid: IRoom['_id'], mid: IMessage['_id']): ContextType => { @@ -21,8 +21,7 @@ export const useMessageBlockContextValue = (rid: IRoom['_id'], mid: IMessage['_i const isRinging = useVideoConfIsRinging(); const dispatchWarning = useVideoConfWarning(); const dispatchPopup = useVideoConfDispatchOutgoing(); - - const videoConfManager = useVideoConfManager(); + const loadVideoConfCapabilities = useVideoConfLoadCapabilities(); const handleOpenVideoConf = useEffectEvent(async (rid: IRoom['_id']) => { if (isCalling || isRinging) { @@ -30,7 +29,7 @@ export const useMessageBlockContextValue = (rid: IRoom['_id'], mid: IMessage['_i } try { - await videoConfManager?.loadCapabilities(); + await loadVideoConfCapabilities(); dispatchPopup({ rid }); } catch (error: any) { dispatchWarning(error.error); diff --git a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfListItem.tsx b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfListItem.tsx index 921ea98c3047..4d5c217dbf78 100644 --- a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfListItem.tsx +++ b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfList/VideoConfListItem.tsx @@ -4,9 +4,9 @@ import { Button, Message, Box, Avatar, Palette, IconButton, ButtonGroup } from ' import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { UserAvatar } from '@rocket.chat/ui-avatar'; import { useTranslation, useSetting } from '@rocket.chat/ui-contexts'; +import { useVideoConfJoinCall } from '@rocket.chat/ui-video-conf'; import type { ReactElement } from 'react'; -import { useVideoConfJoinCall } from '../../../../../contexts/VideoConfContext'; import { useTimeAgo } from '../../../../../hooks/useTimeAgo'; import { VIDEOCONF_STACK_MAX_USERS } from '../../../../../lib/constants'; import { useGoToRoom } from '../../../hooks/useGoToRoom'; diff --git a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/IncomingPopup.tsx b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/IncomingPopup.tsx index 8c8d3c5cf35f..bbf197b7223d 100644 --- a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/IncomingPopup.tsx +++ b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/IncomingPopup.tsx @@ -2,6 +2,7 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { Skeleton } from '@rocket.chat/fuselage'; import { useEffectEvent } from '@rocket.chat/fuselage-hooks'; import { + useVideoConfSetPreferences, VideoConfPopup, VideoConfPopupContent, VideoConfPopupControllers, @@ -18,7 +19,6 @@ import { useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import VideoConfPopupRoomInfo from './VideoConfPopupRoomInfo'; -import { useVideoConfSetPreferences } from '../../../../../../contexts/VideoConfContext'; import { AsyncStatePhase } from '../../../../../../hooks/useAsyncState'; import { useEndpointData } from '../../../../../../hooks/useEndpointData'; diff --git a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/OutgoingPopup.tsx b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/OutgoingPopup.tsx index 1eafe72461db..21ecbfa86e72 100644 --- a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/OutgoingPopup.tsx +++ b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/OutgoingPopup.tsx @@ -10,12 +10,13 @@ import { VideoConfPopupFooterButtons, VideoConfPopupTitle, VideoConfPopupHeader, + useVideoConfCapabilities, + useVideoConfPreferences, } from '@rocket.chat/ui-video-conf'; import type { ReactElement } from 'react'; import { useTranslation } from 'react-i18next'; import VideoConfPopupRoomInfo from './VideoConfPopupRoomInfo'; -import { useVideoConfCapabilities, useVideoConfPreferences } from '../../../../../../contexts/VideoConfContext'; type OutgoingPopupProps = { id: string; diff --git a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/StartCallPopup.tsx b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/StartCallPopup.tsx index 987c7c74d0fe..472660b7f648 100644 --- a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/StartCallPopup.tsx +++ b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/StartCallPopup.tsx @@ -11,13 +11,15 @@ import { VideoConfPopupFooter, VideoConfPopupTitle, VideoConfPopupFooterButtons, + useVideoConfSetPreferences, + useVideoConfCapabilities, + useVideoConfPreferences, } from '@rocket.chat/ui-video-conf'; import type { ReactElement } from 'react'; import { useCallback, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import VideoConfPopupRoomInfo from './VideoConfPopupRoomInfo'; -import { useVideoConfSetPreferences, useVideoConfCapabilities, useVideoConfPreferences } from '../../../../../../contexts/VideoConfContext'; import { useVideoConfRoomName } from '../../hooks/useVideoConfRoomName'; type StartCallPopupProps = { diff --git a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/TimedVideoConfPopup.tsx b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/TimedVideoConfPopup.tsx index f40f0c3596c4..0cd91448563d 100644 --- a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/TimedVideoConfPopup.tsx +++ b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopup/TimedVideoConfPopup.tsx @@ -1,11 +1,5 @@ import type { IRoom } from '@rocket.chat/core-typings'; import { useUserRoom } from '@rocket.chat/ui-contexts'; -import type { ReactElement } from 'react'; -import { useState } from 'react'; - -import IncomingPopup from './IncomingPopup'; -import OutgoingPopup from './OutgoingPopup'; -import StartCallPopup from './StartCallPopup'; import { useVideoConfAcceptCall, useVideoConfAbortCall, @@ -13,7 +7,13 @@ import { useVideoConfDismissCall, useVideoConfStartCall, useVideoConfDismissOutgoing, -} from '../../../../../../contexts/VideoConfContext'; +} from '@rocket.chat/ui-video-conf'; +import type { ReactElement } from 'react'; +import { useState } from 'react'; + +import IncomingPopup from './IncomingPopup'; +import OutgoingPopup from './OutgoingPopup'; +import StartCallPopup from './StartCallPopup'; type TimedVideoConfPopupProps = { id: string; diff --git a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopups.tsx b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopups.tsx index b1391387e732..d985cc03a0f7 100644 --- a/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopups.tsx +++ b/apps/meteor/client/views/room/contextualBar/VideoConference/VideoConfPopups/VideoConfPopups.tsx @@ -1,12 +1,16 @@ import { useCustomSound } from '@rocket.chat/ui-contexts'; -import { VideoConfPopupBackdrop } from '@rocket.chat/ui-video-conf'; +import type { VideoConfPopupPayload } from '@rocket.chat/ui-video-conf'; +import { + VideoConfPopupBackdrop, + useVideoConfIsCalling, + useVideoConfIsRinging, + useVideoConfIncomingCalls, +} from '@rocket.chat/ui-video-conf'; import type { ReactElement } from 'react'; import { useEffect, useMemo } from 'react'; import { FocusScope } from 'react-aria'; import VideoConfPopup from './VideoConfPopup'; -import type { VideoConfPopupPayload } from '../../../../../contexts/VideoConfContext'; -import { useVideoConfIsCalling, useVideoConfIsRinging, useVideoConfIncomingCalls } from '../../../../../contexts/VideoConfContext'; import VideoConfPopupPortal from '../../../../../portals/VideoConfPopupPortal'; const VideoConfPopups = ({ children }: { children?: VideoConfPopupPayload }): ReactElement => { diff --git a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useVideoCallAction.tsx b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useVideoCallAction.tsx index 18af1650e2c4..626effa7b81b 100644 --- a/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useVideoCallAction.tsx +++ b/apps/meteor/client/views/room/hooks/useUserInfoActions/actions/useVideoCallAction.tsx @@ -1,9 +1,9 @@ import type { IUser } from '@rocket.chat/core-typings'; import { isRoomFederated } from '@rocket.chat/core-typings'; import { useTranslation, useUserRoom, useUserId, useUserSubscriptionByName, useSetting, usePermission } from '@rocket.chat/ui-contexts'; +import { useVideoConfDispatchOutgoing, useVideoConfIsCalling, useVideoConfIsRinging } from '@rocket.chat/ui-video-conf'; import { useMemo } from 'react'; -import { useVideoConfDispatchOutgoing, useVideoConfIsCalling, useVideoConfIsRinging } from '../../../../../contexts/VideoConfContext'; import { VideoConfManager } from '../../../../../lib/VideoConfManager'; import { useUserCard } from '../../../contexts/UserCardContext'; import { useVideoConfWarning } from '../../../contextualBar/VideoConference/hooks/useVideoConfWarning'; diff --git a/packages/core-typings/src/IVideoConference.ts b/packages/core-typings/src/IVideoConference.ts index c3c084865156..0d9936c8dfba 100644 --- a/packages/core-typings/src/IVideoConference.ts +++ b/packages/core-typings/src/IVideoConference.ts @@ -4,6 +4,27 @@ import type { IRoom } from './IRoom'; import type { IUser } from './IUser'; import type { AtLeast } from './utils'; +export type DirectCallParams = { + uid: IUser['_id']; + rid: IRoom['_id']; + callId: string; +}; + +export type DirectCallData = DirectCallParams & { + dismissed: boolean; +}; + +export type ProviderCapabilities = { + mic?: boolean; + cam?: boolean; + title?: boolean; +}; + +export type CallPreferences = { + mic?: boolean; + cam?: boolean; +}; + export enum VideoConferenceStatus { CALLING = 0, STARTED = 1, diff --git a/packages/ui-video-conf/package.json b/packages/ui-video-conf/package.json index 489c2ecb186f..716054429f08 100644 --- a/packages/ui-video-conf/package.json +++ b/packages/ui-video-conf/package.json @@ -61,7 +61,8 @@ "@rocket.chat/ui-avatar": "10.0.1", "@rocket.chat/ui-contexts": "14.0.1", "react": "~17.0.2", - "react-dom": "^17.0.2" + "react-dom": "^17.0.2", + "use-sync-external-store": "^1.2.0" }, "volta": { "extends": "../../package.json" diff --git a/packages/ui-video-conf/src/VideoConfContext.ts b/packages/ui-video-conf/src/VideoConfContext.ts new file mode 100644 index 000000000000..d9c0a7c3ac22 --- /dev/null +++ b/packages/ui-video-conf/src/VideoConfContext.ts @@ -0,0 +1,43 @@ +import type { IRoom, DirectCallData, ProviderCapabilities, CallPreferences } from '@rocket.chat/core-typings'; +import { createContext } from 'react'; + +export type VideoConfPopupPayload = { + id: string; + rid: IRoom['_id']; + isReceiving?: boolean; +}; + +type VideoConfContextValue = { + dispatchOutgoing: (options: Omit) => void; + dismissOutgoing: () => void; + startCall: (rid: IRoom['_id'], title?: string) => void; + acceptCall: (callId: string) => void; + joinCall: (callId: string) => void; + dismissCall: (callId: string) => void; + rejectIncomingCall: (callId: string) => void; + abortCall: () => void; + setPreferences: (prefs: { mic?: boolean; cam?: boolean }) => void; + loadCapabilities: () => Promise; + queryIncomingCalls: { + subscribe: (cb: () => void) => () => void; + getSnapshot: () => DirectCallData[]; + }; + queryRinging: { + subscribe: (cb: () => void) => () => void; + getSnapshot: () => boolean; + }; + queryCalling: { + subscribe: (cb: () => void) => () => void; + getSnapshot: () => boolean; + }; + queryCapabilities: { + subscribe: (cb: () => void) => () => void; + getSnapshot: () => ProviderCapabilities; + }; + queryPreferences: { + subscribe: (cb: () => void) => () => void; + getSnapshot: () => CallPreferences; + }; +}; + +export const VideoConfContext = createContext(undefined); diff --git a/packages/ui-video-conf/src/hooks/index.ts b/packages/ui-video-conf/src/hooks/index.ts index 60256b341935..2b90e018822a 100644 --- a/packages/ui-video-conf/src/hooks/index.ts +++ b/packages/ui-video-conf/src/hooks/index.ts @@ -1 +1,2 @@ export * from './useVideoConfControllers'; +export * from './useVideoConfContext'; diff --git a/packages/ui-video-conf/src/hooks/useVideoConfContext.ts b/packages/ui-video-conf/src/hooks/useVideoConfContext.ts new file mode 100644 index 000000000000..fe6b17eabc1e --- /dev/null +++ b/packages/ui-video-conf/src/hooks/useVideoConfContext.ts @@ -0,0 +1,49 @@ +import { useContext } from 'react'; +import { useSyncExternalStore } from 'use-sync-external-store/shim'; + +import { VideoConfContext } from '../VideoConfContext'; + +const useVideoConfContext = () => { + const context = useContext(VideoConfContext); + if (!context) { + throw new Error('Must be running in VideoConf Context'); + } + + return context; +}; + +export const useVideoConfDispatchOutgoing = () => useVideoConfContext().dispatchOutgoing; +export const useVideoConfDismissOutgoing = () => useVideoConfContext().dismissOutgoing; +export const useVideoConfStartCall = () => useVideoConfContext().startCall; +export const useVideoConfAcceptCall = () => useVideoConfContext().acceptCall; +export const useVideoConfJoinCall = () => useVideoConfContext().joinCall; +export const useVideoConfDismissCall = () => useVideoConfContext().dismissCall; +export const useVideoConfAbortCall = () => useVideoConfContext().abortCall; +export const useVideoConfRejectIncomingCall = () => useVideoConfContext().rejectIncomingCall; +export const useVideoConfSetPreferences = () => useVideoConfContext().setPreferences; +export const useVideoConfLoadCapabilities = () => useVideoConfContext().loadCapabilities; + +export const useVideoConfIncomingCalls = () => { + const { queryIncomingCalls } = useVideoConfContext(); + return useSyncExternalStore(queryIncomingCalls.subscribe, queryIncomingCalls.getSnapshot); +}; + +export const useVideoConfIsRinging = () => { + const { queryRinging } = useVideoConfContext(); + return useSyncExternalStore(queryRinging.subscribe, queryRinging.getSnapshot); +}; + +export const useVideoConfIsCalling = () => { + const { queryCalling } = useVideoConfContext(); + return useSyncExternalStore(queryCalling.subscribe, queryCalling.getSnapshot); +}; + +export const useVideoConfCapabilities = () => { + const { queryCapabilities } = useVideoConfContext(); + return useSyncExternalStore(queryCapabilities.subscribe, queryCapabilities.getSnapshot); +}; + +export const useVideoConfPreferences = () => { + const { queryPreferences } = useVideoConfContext(); + return useSyncExternalStore(queryPreferences.subscribe, queryPreferences.getSnapshot); +}; diff --git a/packages/ui-video-conf/src/index.ts b/packages/ui-video-conf/src/index.ts index d331731d0f44..582fbab4b27b 100644 --- a/packages/ui-video-conf/src/index.ts +++ b/packages/ui-video-conf/src/index.ts @@ -1,6 +1,7 @@ import VideoConfButton from './VideoConfButton'; import VideoConfController from './VideoConfController'; +export * from './VideoConfContext'; export * from './VideoConfMessage'; export * from './VideoConfPopup'; export * from './hooks'; diff --git a/yarn.lock b/yarn.lock index 01d64ea39f7f..8ef2777c4098 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9461,6 +9461,7 @@ __metadata: "@rocket.chat/ui-contexts": 14.0.1 react: ~17.0.2 react-dom: ^17.0.2 + use-sync-external-store: ^1.2.0 languageName: unknown linkType: soft