Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ type DeviceAccessStatus = {
camera: boolean | undefined;
};

type PublishingErrorType = {
export type PublishingErrorType = {
header: string;
caption: string;
} | null;
Expand Down Expand Up @@ -272,7 +272,7 @@ const usePublisher = (): PublisherContextType => {
await sessionPublish(publisherRef.current);
} catch (err: unknown) {
if (err instanceof Error) {
console.warn(err.stack);
console.warn(err);
setIsPublishingToSession(false);
publish();
}
Expand Down
13 changes: 12 additions & 1 deletion frontend/src/Context/SessionProvider/session.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export type SessionContextType = {
publish: (publisher: Publisher) => Promise<void>;
unpublish: (publisher: Publisher) => void;
lastStreamUpdate: StreamPropertyChangedEvent | null;
subscriptionError: Error | null;
};

/**
Expand Down Expand Up @@ -101,6 +102,7 @@ export const SessionContext = createContext<SessionContextType>({
publish: async () => Promise.resolve(),
unpublish: () => {},
lastStreamUpdate: null,
subscriptionError: null,
});

export type ConnectionEventType = {
Expand Down Expand Up @@ -133,6 +135,7 @@ const SessionProvider = ({ children }: SessionProviderProps): ReactElement => {
const vonageVideoClient = useRef<null | VonageVideoClient>(null);
const [reconnecting, setReconnecting] = useState(false);
const [subscriberWrappers, setSubscriberWrappers] = useState<SubscriberWrapper[]>([]);
const [subscriptionError, setSubscriptionError] = useState<Error | null>(null);
const [ownCaptions, setOwnCaptions] = useState<string | null>(null);
const [layoutMode, setLayoutMode] = useState<LayoutMode>(
config.meetingRoomSettings.defaultLayoutMode
Expand Down Expand Up @@ -308,6 +311,10 @@ const SessionProvider = ({ children }: SessionProviderProps): ReactElement => {
});
};

const handleSubscriptionError = useCallback((error: unknown) => {
setSubscriptionError(error instanceof Error ? error : new Error('Unknown subscription error'));
}, []);

/**
* Connects to the session using the provided credentials.
* @param {Credential} credential - The credentials for the session.
Expand Down Expand Up @@ -340,6 +347,8 @@ const SessionProvider = ({ children }: SessionProviderProps): ReactElement => {
);
vonageVideoClient.current.on('subscriberDestroyed', handleSubscriberDestroyed);
vonageVideoClient.current.on('localCaptionReceived', handleLocalCaptionReceived);
vonageVideoClient.current.on('subscriptionError', handleSubscriptionError);

await vonageVideoClient.current.connect();
setConnected(true);
} catch (err: unknown) {
Expand All @@ -358,7 +367,7 @@ const SessionProvider = ({ children }: SessionProviderProps): ReactElement => {
async (roomName: string) => {
fetchCredentials(roomName)
.then((credentials) => {
connect(credentials.data);
return connect(credentials.data);
})
.catch(console.warn);
},
Expand Down Expand Up @@ -442,6 +451,7 @@ const SessionProvider = ({ children }: SessionProviderProps): ReactElement => {
publish,
unpublish,
lastStreamUpdate,
subscriptionError,
}),
[
activeSpeakerId,
Expand Down Expand Up @@ -472,6 +482,7 @@ const SessionProvider = ({ children }: SessionProviderProps): ReactElement => {
publish,
unpublish,
lastStreamUpdate,
subscriptionError,
]
);

Expand Down
2 changes: 2 additions & 0 deletions frontend/src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@
"publishingErrors.accessDenied.title": "{{device}} access is denied",
"publishingErrors.blocked.message": "We're having trouble connecting you with others in the meeting room. Please check your network and try again.",
"publishingErrors.blocked.title": "Difficulties joining room",
"subscribingErrors.blocked.message": "We're having trouble connecting you with others in the meeting room. Please check your network and try again.",
"subscribingErrors.blocked.title": "Difficulties joining room",
"recording.start.dialog.content": "Make sure everyone is ready! You can download the recording from the \"Goodbye\" page after you leave the room.",
"recording.start.dialog.title": "Start Recording?",
"recording.start.title": "Start recording",
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/locales/es-MX.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@
"publishingErrors.accessDenied.title": "Acceso a {{device}} denegado",
"publishingErrors.blocked.message": "No pudimos conectarte con la sala. Revisa tu conexión e inténtalo de nuevo.",
"publishingErrors.blocked.title": "Problemas para entrar a la sala",
"subscribingErrors.blocked.message": "No pudimos conectarte con la sala. Revisa tu conexión e inténtalo de nuevo.",
"subscribingErrors.blocked.title": "Problemas para entrar a la sala",
"recording.start.dialog.content": "¡Asegúrate que todos estén listos! Podrás descargar la grabación en la página de \"Despedida\" al salir.",
"recording.start.dialog.title": "¿Iniciar grabación?",
"recording.start.title": "Iniciar grabación",
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@
"publishingErrors.accessDenied.title": "Acceso a {{device}} denegado",
"publishingErrors.blocked.message": "Tenemos problemas para conectarte con otros en la sala. Por favor revisa tu red e intenta de nuevo.",
"publishingErrors.blocked.title": "Dificultades para unirse a la sala",
"subscribingErrors.blocked.message": "Tenemos problemas para conectarte con otros en la sala. Por favor revisa tu red e intenta de nuevo.",
"subscribingErrors.blocked.title": "Dificultades para unirse a la sala",
"recording.start.dialog.content": "¡Asegúrate de que todos estén listos! Puedes descargar la grabación desde la página de \"Despedida\" después de salir de la sala.",
"recording.start.dialog.title": "¿Iniciar grabación?",
"recording.start.title": "Iniciar grabación",
Expand Down
2 changes: 2 additions & 0 deletions frontend/src/locales/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@
"publishingErrors.accessDenied.title": "L'accesso a {{device}} è stato negato",
"publishingErrors.blocked.message": "Problemi di connessione con altri nella stanza. Controlla la rete e riprova.",
"publishingErrors.blocked.title": "Difficoltà a unirsi alla stanza",
"subscribingErrors.blocked.message": "Problemi di connessione con altri nella stanza. Controlla la rete e riprova.",
"subscribingErrors.blocked.title": "Difficoltà a unirsi alla stanza",
"recording.start.dialog.content": "Assicurati che tutti siano pronti! Puoi scaricare la registrazione dalla pagina \"Addio\" dopo aver lasciato la stanza.",
"recording.start.dialog.title": "Avvia registrazione?",
"recording.start.title": "Avvia registrazione",
Expand Down
73 changes: 58 additions & 15 deletions frontend/src/pages/MeetingRoom/MeetingRoom.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import useIsSmallViewport from '../../hooks/useIsSmallViewport';
import CaptionsError from '../../components/MeetingRoom/CaptionsError';
import useBackgroundPublisherContext from '../../hooks/useBackgroundPublisherContext';
import { DEVICE_ACCESS_STATUS } from '../../utils/constants';
import type { PublishingErrorType } from '../../Context/PublisherProvider/usePublisher/usePublisher';

const height = '@apply h-[calc(100dvh_-_80px)]';

Expand All @@ -44,6 +45,7 @@ const MeetingRoom = (): ReactElement => {

const {
joinRoom,
subscriptionError,
subscriberWrappers,
connected,
disconnect,
Expand All @@ -57,7 +59,6 @@ const MeetingRoom = (): ReactElement => {
} = useSessionContext();
const { isSharingScreen, screensharingPublisher, screenshareVideoElement, toggleShareScreen } =
useScreenShare();
const navigate = useNavigate();
const publisherOptions = usePublisherOptions();
const isSmallViewport = useIsSmallViewport();

Expand Down Expand Up @@ -109,20 +110,11 @@ const MeetingRoom = (): ReactElement => {
}
}, [accessStatus]);

// If the user is unable to publish, we redirect them to the goodbye page.
// This prevents users from subscribing to other participants in the room, and being unable to communicate with them.
useEffect(() => {
if (publishingError) {
const { header, caption } = publishingError;
navigate('/goodbye', {
state: {
header,
caption,
roomName,
},
});
}
}, [publishingError, navigate, roomName]);
// eslint-disable-next-line @typescript-eslint/no-use-before-define
useRedirectOnPublisherError(publishingError);

// eslint-disable-next-line @typescript-eslint/no-use-before-define
useRedirectOnSubscriberError(subscriptionError);

return (
<div data-testid="meetingRoom" className={`${height} w-screen bg-darkGray-100`}>
Expand Down Expand Up @@ -174,4 +166,55 @@ const MeetingRoom = (): ReactElement => {
);
};

/**
* If the user is unable to publish, we redirect them to the goodbye page.
* This prevents users from subscribing to other participants in the room, and being unable to communicate with them.
* @param {PublishingErrorType | null} publishingError - The publishing error object or null if no error.
*/
function useRedirectOnPublisherError(publishingError: PublishingErrorType | null) {
const navigate = useNavigate();
const roomName = useRoomName();
const { t } = useTranslation();

useEffect(() => {
if (!publishingError) {
return;
}

const { header, caption } = publishingError;
navigate('/goodbye', {
state: {
header,
caption,
roomName,
},
});
}, [publishingError, navigate, roomName, t]);
}

/**
* If the user is unable to subscribe, we redirect them to the goodbye page.
* This prevents users from subscribing to other participants in the room, and being unable to communicate with them.
* @param {Error | null} subscriberError - The subscriber error object or null if no error.
*/
function useRedirectOnSubscriberError(subscriberError: Error | null) {
const navigate = useNavigate();
const roomName = useRoomName();
const { t } = useTranslation();

useEffect(() => {
if (!subscriberError) {
return;
}

navigate('/goodbye', {
state: {
header: t('subscribingErrors.blocked.title'),
caption: t('subscribingErrors.blocked.message'),
roomName,
},
});
}, [subscriberError, navigate, roomName, t]);
}

export default MeetingRoom;
36 changes: 25 additions & 11 deletions frontend/src/utils/VonageVideoClient/vonageVideoClient.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import EventEmitter from 'events';
import logOnConnect from '../logOnConnect';
import VonageVideoClient from './vonageVideoClient';
import { Credential, SignalEvent, SignalType } from '../../types/session';
import { wait } from '../idempotentCallbackWithRetry/idempotentCallbackWithRetry';

vi.mock('../logOnConnect');
vi.mock('@vonage/client-sdk-video');
Expand Down Expand Up @@ -115,19 +116,32 @@ describe('VonageVideoClient', () => {
});
}));

it('emits an event for screenshare subscribers', () =>
new Promise<void>((done) => {
const streamId = 'stream-id';
vonageVideoClient?.connect().then(() => {
vonageVideoClient?.on('screenshareStreamCreated', () => {
done();
});
it('emits an event for screenshare subscribers', async () => {
expect.assertions(1);
const streamId = 'stream-id';

mockSession.emit('streamCreated', {
stream: { streamId, videoType: 'screen' } as unknown as Stream,
vonageVideoClient?.on('screenshareStreamCreated', () => {
expect(true).toBe(true);
});

await vonageVideoClient?.connect();

vi.spyOn(vonageVideoClient!.clientSession!, 'subscribe').mockImplementation(
(_a, _b, _c, callback) => {
queueMicrotask(() => {
callback!();
});
});
}));

return mockSubscriber;
}
);

mockSession.emit('streamCreated', {
stream: { streamId, videoType: 'screen' } as unknown as Stream,
});

await wait(10);
});

it('emits an event containing the streamId when the stream is destroyed', async () => {
const streamId = 'stream-id';
Expand Down
Loading
Loading