diff --git a/apps/meteor/client/views/conference/ConferenceChat.tsx b/apps/meteor/client/views/conference/ConferenceChat.tsx
new file mode 100644
index 0000000000000..6991e7769a9f0
--- /dev/null
+++ b/apps/meteor/client/views/conference/ConferenceChat.tsx
@@ -0,0 +1,34 @@
+import type { RoomType } from '@rocket.chat/core-typings';
+import { Box, Button } from '@rocket.chat/fuselage';
+import { useState } from 'react';
+
+import RoomOpenerEmbedded from '../room/RoomOpenerEmbedded';
+import EmbeddedPreload from '../root/MainLayout/EmbeddedPreload';
+
+type ConferenceChatProps = {
+ type: RoomType;
+ reference: string;
+ loading: boolean;
+};
+const ConferenceChat = ({ type, loading }: ConferenceChatProps) => {
+ const [reference, setReference] = useState('general');
+
+ if (loading) {
+ return
Loading...
;
+ }
+
+ return (
+
+
+ {/* Temporary buttons to test room change */}
+
+
+
+
+
+
+
+ );
+};
+
+export default ConferenceChat;
diff --git a/apps/meteor/client/views/conference/ConferenceIframe.tsx b/apps/meteor/client/views/conference/ConferenceIframe.tsx
new file mode 100644
index 0000000000000..0adc754faf83c
--- /dev/null
+++ b/apps/meteor/client/views/conference/ConferenceIframe.tsx
@@ -0,0 +1,27 @@
+type ConferenceIframeProps = {
+ url: string | undefined;
+ loading: boolean;
+};
+
+const ConferenceIframe = ({ url, loading }: ConferenceIframeProps) => {
+ if (!url) {
+ return No conference URL provided.
;
+ }
+
+ if (loading) {
+ return Loading...
;
+ }
+
+ return (
+
+ );
+};
+
+export default ConferenceIframe;
diff --git a/apps/meteor/client/views/conference/ConferenceInlinePage.tsx b/apps/meteor/client/views/conference/ConferenceInlinePage.tsx
new file mode 100644
index 0000000000000..a03abe1f5c998
--- /dev/null
+++ b/apps/meteor/client/views/conference/ConferenceInlinePage.tsx
@@ -0,0 +1,27 @@
+import { Box } from '@rocket.chat/fuselage';
+
+import ConferenceChat from './ConferenceChat';
+import ConferenceIframe from './ConferenceIframe';
+import { useConferenceEmbedded } from './hooks/useConferenceEmbedded';
+
+type ConferenceInlinePageProps = {
+ callId: string;
+};
+
+const ConferenceInlinePage = ({ callId }: ConferenceInlinePageProps) => {
+ const { room, conference } = useConferenceEmbedded(callId);
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default ConferenceInlinePage;
diff --git a/apps/meteor/client/views/conference/ConferencePage.tsx b/apps/meteor/client/views/conference/ConferencePage.tsx
index 5717f4d823c74..d2dec357586f4 100644
--- a/apps/meteor/client/views/conference/ConferencePage.tsx
+++ b/apps/meteor/client/views/conference/ConferencePage.tsx
@@ -1,11 +1,9 @@
-import { useUserDisplayName } from '@rocket.chat/ui-client';
-import { useRoute, useSetModal, useUser } from '@rocket.chat/ui-contexts';
+import { useRouteParameter } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
-import { useEffect } from 'react';
+import ConferenceInlinePage from './ConferenceInlinePage';
import ConferencePageError from './ConferencePageError';
-import { useVideoConfOpenCall } from '../room/contextualBar/VideoConference/hooks/useVideoConfOpenCall';
-import PageLoading from '../root/PageLoading';
+import ConferenceRedirectPage from './ConferenceRedirectPage';
const getQueryParams = () => {
const queryString = window.location.search;
@@ -16,30 +14,18 @@ const getQueryParams = () => {
};
const ConferencePage = (): ReactElement => {
- const user = useUser();
- const defaultRoute = useRoute('home');
- const setModal = useSetModal();
- const handleOpenCall = useVideoConfOpenCall();
- const userDisplayName = useUserDisplayName({ name: user?.name, username: user?.username });
-
+ const id = useRouteParameter('id');
const { callUrlParam } = getQueryParams();
- const callUrl = callUrlParam && userDisplayName ? `${callUrlParam}&name=${userDisplayName}` : callUrlParam;
-
- useEffect(() => {
- if (!callUrl) {
- return;
- }
- handleOpenCall(callUrl);
-
- defaultRoute.push();
- }, [setModal, defaultRoute, callUrl, handleOpenCall, userDisplayName]);
+ if (callUrlParam) {
+ return ;
+ }
- if (!callUrl) {
- return ;
+ if (id) {
+ return ;
}
- return ;
+ return ;
};
export default ConferencePage;
diff --git a/apps/meteor/client/views/conference/ConferenceRedirectPage.tsx b/apps/meteor/client/views/conference/ConferenceRedirectPage.tsx
new file mode 100644
index 0000000000000..8734d4a4e9b13
--- /dev/null
+++ b/apps/meteor/client/views/conference/ConferenceRedirectPage.tsx
@@ -0,0 +1,32 @@
+import { useRoute, useSetModal } from '@rocket.chat/ui-contexts';
+import { useEffect } from 'react';
+
+import { useVideoConfOpenCall } from '../room/contextualBar/VideoConference/hooks/useVideoConfOpenCall';
+import PageLoading from '../root/PageLoading';
+import { useConferenceCallUrl } from './hooks/useConferenceCallUrl';
+
+type ConferenceRedirectPageProps = {
+ callUrl: string;
+};
+
+const ConferenceRedirectPage = ({ callUrl: baseCallUrl }: ConferenceRedirectPageProps) => {
+ const defaultRoute = useRoute('home');
+ const setModal = useSetModal();
+ const handleOpenCall = useVideoConfOpenCall();
+ const getConferenceCallUrl = useConferenceCallUrl();
+
+ useEffect(() => {
+ if (!baseCallUrl) {
+ return;
+ }
+
+ const callUrl = getConferenceCallUrl(baseCallUrl);
+ handleOpenCall(callUrl);
+
+ defaultRoute.push();
+ }, [setModal, defaultRoute, baseCallUrl, handleOpenCall, getConferenceCallUrl]);
+
+ return ;
+};
+
+export default ConferenceRedirectPage;
diff --git a/apps/meteor/client/views/conference/hooks/useConferenceCallUrl.ts b/apps/meteor/client/views/conference/hooks/useConferenceCallUrl.ts
new file mode 100644
index 0000000000000..b107d5323b790
--- /dev/null
+++ b/apps/meteor/client/views/conference/hooks/useConferenceCallUrl.ts
@@ -0,0 +1,16 @@
+import { useUserDisplayName } from '@rocket.chat/ui-client';
+import { useUser } from '@rocket.chat/ui-contexts';
+
+export const useConferenceCallUrl = () => {
+ const user = useUser();
+ const userDisplayName = useUserDisplayName({ name: user?.name, username: user?.username });
+
+ return (callUrl: string) => {
+ if (!userDisplayName) {
+ return callUrl;
+ }
+ const url = new URL(callUrl);
+ url.searchParams.set('name', userDisplayName);
+ return url.toString();
+ };
+};
diff --git a/apps/meteor/client/views/conference/hooks/useConferenceEmbedded.tsx b/apps/meteor/client/views/conference/hooks/useConferenceEmbedded.tsx
new file mode 100644
index 0000000000000..6170bc84811d5
--- /dev/null
+++ b/apps/meteor/client/views/conference/hooks/useConferenceEmbedded.tsx
@@ -0,0 +1,23 @@
+import { useEndpoint } from '@rocket.chat/ui-contexts';
+import { useQuery } from '@tanstack/react-query';
+
+import { useConferenceCallUrl } from './useConferenceCallUrl';
+
+export const useConferenceEmbedded = (callId: string) => {
+ const joinConference = useEndpoint('POST', '/v1/video-conference.join');
+ const getConferenceCallUrl = useConferenceCallUrl();
+
+ const { data, isPending } = useQuery({
+ queryKey: ['conference-embedded', callId],
+ queryFn: async () => joinConference({ callId, state: { mic: true, cam: false } }),
+ });
+
+ return {
+ room: { type: 'c', reference: 'general', loading: isPending } as const,
+ conference: {
+ url: data?.url ? getConferenceCallUrl(data.url) : undefined,
+ providerName: data?.providerName,
+ loading: isPending,
+ } as const,
+ };
+};
diff --git a/apps/meteor/client/views/room/RoomOpenerEmbedded.tsx b/apps/meteor/client/views/room/RoomOpenerEmbedded.tsx
index aaeee3f060cdc..265faa86faea9 100644
--- a/apps/meteor/client/views/room/RoomOpenerEmbedded.tsx
+++ b/apps/meteor/client/views/room/RoomOpenerEmbedded.tsx
@@ -1,9 +1,9 @@
import type { ISubscription, RoomType } from '@rocket.chat/core-typings';
import { Box, States, StatesIcon, StatesSubtitle, StatesTitle } from '@rocket.chat/fuselage';
import { Header } from '@rocket.chat/ui-client';
-import { useStream, useUserId } from '@rocket.chat/ui-contexts';
+import { LayoutContext, useLayout, useStream, useUserId } from '@rocket.chat/ui-contexts';
import type { ReactElement } from 'react';
-import { lazy, Suspense, useEffect } from 'react';
+import { lazy, Suspense, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import NotSubscribedRoom from './NotSubscribedRoom';
@@ -31,6 +31,8 @@ const RoomOpenerEmbedded = ({ type, reference }: RoomOpenerProps): ReactElement
const { data, error, isSuccess, isError, isLoading } = useOpenRoom({ type, reference });
const uid = useUserId();
const subscribeToNotifyUser = useStream('notify-user');
+ const layoutContext = useLayout();
+ const layoutContextEmbedded = useMemo(() => ({ ...layoutContext, isEmbedded: true }), [layoutContext]);
const rid = data?.rid;
@@ -51,47 +53,49 @@ const RoomOpenerEmbedded = ({ type, reference }: RoomOpenerProps): ReactElement
const { t } = useTranslation();
return (
-
- }>
- {isLoading && }
- {isSuccess && (
-
-
-
- )}
- {isError &&
- (() => {
- if (error instanceof OldUrlRoomError) {
- return ;
- }
+
+
+ }>
+ {isLoading && }
+ {isSuccess && (
+
+
+
+ )}
+ {isError &&
+ (() => {
+ if (error instanceof OldUrlRoomError) {
+ return ;
+ }
- if (error instanceof RoomNotFoundError) {
- return ;
- }
+ if (error instanceof RoomNotFoundError) {
+ return ;
+ }
- if (error instanceof NotSubscribedToRoomError) {
- return ;
- }
+ if (error instanceof NotSubscribedToRoomError) {
+ return ;
+ }
- if (error instanceof NotAuthorizedError) {
- return ;
- }
+ if (error instanceof NotAuthorizedError) {
+ return ;
+ }
- return (
- }
- body={
-
-
- {t('core.Error')}
- {getErrorMessage(error)}
-
- }
- />
- );
- })()}
-
-
+ return (
+ }
+ body={
+
+
+ {t('core.Error')}
+ {getErrorMessage(error)}
+
+ }
+ />
+ );
+ })()}
+
+
+
);
};
diff --git a/apps/meteor/client/views/root/MainLayout/EmbeddedPreload.tsx b/apps/meteor/client/views/root/MainLayout/EmbeddedPreload.tsx
index 35edbf4e78ee1..7b39e4db9b8a5 100644
--- a/apps/meteor/client/views/root/MainLayout/EmbeddedPreload.tsx
+++ b/apps/meteor/client/views/root/MainLayout/EmbeddedPreload.tsx
@@ -1,3 +1,4 @@
+import type { RoomType } from '@rocket.chat/core-typings';
import { useEndpoint, useMethod, useRouter, useUserId } from '@rocket.chat/ui-contexts';
import { useQuery } from '@tanstack/react-query';
import type { ReactElement, ReactNode } from 'react';
@@ -7,15 +8,20 @@ import { RoomsCachedStore, SubscriptionsCachedStore } from '../../../cachedStore
import { roomsQueryKeys } from '../../../lib/queryKeys';
import { roomCoordinator } from '../../../lib/rooms/roomCoordinator';
import { mapSubscriptionFromApi } from '../../../lib/utils/mapSubscriptionFromApi';
+import { Rooms } from '../../../stores';
import PageLoading from '../PageLoading';
import { useMainReady } from '../hooks/useMainReady';
-const EmbeddedPreload = ({ children }: { children: ReactNode }): ReactElement => {
+const EmbeddedPreload = ({ children, reference, type }: { children: ReactNode; reference?: string; type?: RoomType }): ReactElement => {
const ready = useMainReady();
const router = useRouter();
const uid = useUserId();
const roomParams = useMemo(() => {
+ if (reference && type) {
+ return { reference, type };
+ }
+
const routeName = router.getRouteName();
if (!routeName) {
return null;
@@ -32,14 +38,18 @@ const EmbeddedPreload = ({ children }: { children: ReactNode }): ReactElement =>
}
return directives.extractOpenRoomParams(router.getRouteParameters());
- }, [router]);
+ }, [reference, router, type]);
const getRoomByTypeAndName = useMethod('getRoomByTypeAndName');
const getSubscription = useEndpoint('GET', '/v1/subscriptions.getOne');
const shouldFetch = !!roomParams && !!uid;
- const { isLoading, isSuccess, isError } = useQuery({
+ const {
+ isPending: isLoading,
+ isSuccess,
+ isError,
+ } = useQuery({
queryKey: roomParams ? roomsQueryKeys.roomReference(roomParams.reference, roomParams.type, uid ?? undefined) : [],
queryFn: async () => {
if (!roomParams) {
@@ -47,7 +57,9 @@ const EmbeddedPreload = ({ children }: { children: ReactNode }): ReactElement =>
}
const roomData = await getRoomByTypeAndName(roomParams.type, roomParams.reference);
- if (!roomData?._id) {
+ if (roomData?._id) {
+ Rooms.state.store(roomData);
+ } else {
return null;
}
diff --git a/apps/meteor/client/views/root/MainLayout/MainLayout.tsx b/apps/meteor/client/views/root/MainLayout/MainLayout.tsx
index 907fb5a1df8d9..9cc49e0c10411 100644
--- a/apps/meteor/client/views/root/MainLayout/MainLayout.tsx
+++ b/apps/meteor/client/views/root/MainLayout/MainLayout.tsx
@@ -4,6 +4,7 @@ import { Suspense } from 'react';
import AuthenticationCheck from './AuthenticationCheck';
import EmbeddedPreload from './EmbeddedPreload';
+import LayoutWithSidebar from './LayoutWithSidebar';
import Preload from './Preload';
import { useCustomScript } from './useCustomScript';
@@ -20,7 +21,9 @@ const MainLayout = ({ children = null }: MainLayoutProps): ReactElement => {
return (
- {children}
+
+ {children}
+
);
@@ -29,7 +32,9 @@ const MainLayout = ({ children = null }: MainLayoutProps): ReactElement => {
return (
- {children}
+
+ {children}
+
);
diff --git a/apps/meteor/client/views/root/MainLayout/TwoFactorAuthSetupCheck.tsx b/apps/meteor/client/views/root/MainLayout/TwoFactorAuthSetupCheck.tsx
index 425ba4a776a33..774ae1431c952 100644
--- a/apps/meteor/client/views/root/MainLayout/TwoFactorAuthSetupCheck.tsx
+++ b/apps/meteor/client/views/root/MainLayout/TwoFactorAuthSetupCheck.tsx
@@ -1,15 +1,14 @@
import { Box } from '@rocket.chat/fuselage';
import { useLayout } from '@rocket.chat/ui-contexts';
-import type { ReactElement, ReactNode } from 'react';
+import type { ReactNode } from 'react';
import { lazy } from 'react';
-import LayoutWithSidebar from './LayoutWithSidebar';
import MainContent from './MainContent';
import { useRequire2faSetup } from '../../hooks/useRequire2faSetup';
const AccountSecurityPage = lazy(() => import('../../account/security/AccountSecurityPage'));
-const TwoFactorAuthSetupCheck = ({ children }: { children: ReactNode }): ReactElement => {
+const TwoFactorAuthSetupCheck = ({ children }: { children: ReactNode }): ReactNode => {
const { isEmbedded: embeddedLayout } = useLayout();
const require2faSetup = useRequire2faSetup();
@@ -23,7 +22,7 @@ const TwoFactorAuthSetupCheck = ({ children }: { children: ReactNode }): ReactEl
);
}
- return {children};
+ return children;
};
export default TwoFactorAuthSetupCheck;
diff --git a/apps/meteor/server/services/media-call/service.ts b/apps/meteor/server/services/media-call/service.ts
index bf3c38ebbf392..f6fee7ae7e44d 100644
--- a/apps/meteor/server/services/media-call/service.ts
+++ b/apps/meteor/server/services/media-call/service.ts
@@ -474,9 +474,11 @@ export class MediaCallService extends ServiceClassInternal implements IMediaCall
logger.error({ msg: 'Unexpected error while flagging call as escalated', err });
});
- const result = await VideoConf.joinCall(conference, user, { mic: true, cam: false });
+ await VideoConf.joinCall(conference, user, { mic: true, cam: false });
- return result;
+ const baseUrl = settings.get('Site_Url');
+
+ return `${baseUrl}/conference/${conference._id}`;
}
private async findExistingConferenceForCall(call: IMediaCall): Promise {