diff --git a/package.json b/package.json index b629cbdea321..ce7bf80a03ef 100644 --- a/package.json +++ b/package.json @@ -86,7 +86,7 @@ "js-md5": "0.6.1", "js-sha512": "0.8.0", "jwt-decode": "2.2.0", - "lib-jitsi-meet": "https://github.com/internxt/lib-jitsi-meet/releases/download/v.0.0.20/lib-jitsi-meet-0.0.20.tgz", + "lib-jitsi-meet": "https://github.com/internxt/lib-jitsi-meet/releases/download/v.0.0.20-debug/lib-jitsi-meet-0.0.20-debug.tgz", "lodash-es": "4.17.23", "moment": "2.29.4", "moment-duration-format": "2.2.2", diff --git a/react/features/base/meet/middlewares/connection-stability/connection-notifications/event-handlers.connection.ts b/react/features/base/meet/middlewares/connection-stability/connection-notifications/event-handlers.connection.ts index 7bff10440763..16110c011ce5 100644 --- a/react/features/base/meet/middlewares/connection-stability/connection-notifications/event-handlers.connection.ts +++ b/react/features/base/meet/middlewares/connection-stability/connection-notifications/event-handlers.connection.ts @@ -1,6 +1,5 @@ import { IStore } from '../../../../../app/types'; import { isLeavingConferenceManually } from "../../../general/utils/conferenceState"; -import { isAutoReconnecting } from "../middleware.auto-reconnect"; import { showConnectionFailedNotification, showConnectionLostNotification } from "./notification-helpers"; /** @@ -22,7 +21,6 @@ export const handleXMPPDisconnected = (dispatch: IStore["dispatch"], message: st console.log("[CONNECTION_NOTIFICATIONS] XMPP disconnected:", message); if (isLeavingConferenceManually()) return; - if (isAutoReconnecting()) return; showConnectionLostNotification(dispatch); }; @@ -37,7 +35,6 @@ export const handleXMPPDisconnected = (dispatch: IStore["dispatch"], message: st */ export const handleXMPPConnectionFailed = (dispatch: IStore["dispatch"], error: any, message: string) => { console.error("[CONNECTION_NOTIFICATIONS] XMPP connection failed:", error, message); - if (isAutoReconnecting()) return; showConnectionFailedNotification(dispatch, message); }; diff --git a/react/features/base/meet/middlewares/connection-stability/connection-notifications/index.ts b/react/features/base/meet/middlewares/connection-stability/connection-notifications/index.ts index b464d8ddede8..a2731f04f2e2 100644 --- a/react/features/base/meet/middlewares/connection-stability/connection-notifications/index.ts +++ b/react/features/base/meet/middlewares/connection-stability/connection-notifications/index.ts @@ -4,7 +4,12 @@ import { CONFERENCE_JOINED, CONFERENCE_WILL_LEAVE } from '../../../../conference import { setLeaveConferenceManually } from '../../../general/utils/conferenceState'; import { CONNECTION_WILL_CONNECT } from '../../../../connection/actionTypes'; import MiddlewareRegistry from '../../../../redux/MiddlewareRegistry'; -import { setupConferenceMediaListeners, setupXMPPConnectionListeners } from './listener-setup'; +import { + removeConferenceMediaListeners, + removeXMPPConnectionListeners, + setupConferenceMediaListeners, + setupXMPPConnectionListeners +} from './listener-setup'; import { createConnectionState } from './state'; /** @@ -24,6 +29,7 @@ MiddlewareRegistry.register(({ dispatch }: IStore) => { switch (action.type) { case CONNECTION_WILL_CONNECT: { + console.log("[CONNECTION_NOTIFICATIONS] CONNECTION_WILL_CONNECT - Setting up XMPP listeners"); setLeaveConferenceManually(false); connectionState.hasConnectionListeners = false; @@ -34,6 +40,7 @@ MiddlewareRegistry.register(({ dispatch }: IStore) => { } case CONFERENCE_JOINED: { + console.log("[CONNECTION_NOTIFICATIONS] CONFERENCE_JOINED - Setting up media listeners"); const { conference } = action; setupConferenceMediaListeners(conference, dispatch, connectionState); @@ -41,9 +48,11 @@ MiddlewareRegistry.register(({ dispatch }: IStore) => { } case CONFERENCE_WILL_LEAVE: { - // User clicked hangup button - don't show reconnection notifications + console.log("[CONNECTION_NOTIFICATIONS] CONFERENCE_WILL_LEAVE - Cleaning up all listeners"); + // User clicked hangup button - cleanup listeners to prevent memory leaks + removeConferenceMediaListeners(connectionState); + removeXMPPConnectionListeners(connectionState); setLeaveConferenceManually(true); - connectionState.hasConferenceListeners = false; connectionState.wasMediaConnectionInterrupted = false; break; } diff --git a/react/features/base/meet/middlewares/connection-stability/connection-notifications/listener-setup.ts b/react/features/base/meet/middlewares/connection-stability/connection-notifications/listener-setup.ts index e133dcc815aa..efbd99fd7092 100644 --- a/react/features/base/meet/middlewares/connection-stability/connection-notifications/listener-setup.ts +++ b/react/features/base/meet/middlewares/connection-stability/connection-notifications/listener-setup.ts @@ -26,20 +26,65 @@ export const setupConferenceMediaListeners = ( state: ConnectionState ) => { if (state.hasConferenceListeners || !conference) { + console.log("[LISTENER_SETUP] Skipping conference media listeners setup", { + alreadyHasListeners: state.hasConferenceListeners, + noConference: !conference + }); return; } - conference.addEventListener(JitsiConferenceEvents.CONNECTION_INTERRUPTED, () => - handleMediaConnectionInterrupted(dispatch, state) - ); + console.log("[LISTENER_SETUP] Setting up conference media listeners (ICE events)"); - conference.addEventListener(JitsiConferenceEvents.CONNECTION_RESTORED, () => - handleMediaConnectionRestored(dispatch, state) - ); + const interruptedHandler = () => handleMediaConnectionInterrupted(dispatch, state); + const restoredHandler = () => handleMediaConnectionRestored(dispatch, state); + const suspendHandler = () => handleDeviceSuspended(dispatch); - conference.addEventListener(JitsiConferenceEvents.SUSPEND_DETECTED, () => handleDeviceSuspended(dispatch)); + conference.addEventListener(JitsiConferenceEvents.CONNECTION_INTERRUPTED, interruptedHandler); + conference.addEventListener(JitsiConferenceEvents.CONNECTION_RESTORED, restoredHandler); + conference.addEventListener(JitsiConferenceEvents.SUSPEND_DETECTED, suspendHandler); + state.conferenceHandlers = { + interruptedHandler, + restoredHandler, + suspendHandler + }; + state.conferenceRef = conference; state.hasConferenceListeners = true; + console.log("[LISTENER_SETUP] Conference media listeners registered successfully"); +}; + +/** + * Removes event listeners for conference media connection events + * + * @param state - Connection state containing handler references + */ +export const removeConferenceMediaListeners = (state: ConnectionState) => { + if (!state.conferenceRef || !state.conferenceHandlers) { + console.log("[LISTENER_SETUP] Skipping conference media listeners removal - no refs"); + return; + } + + console.log("[LISTENER_SETUP] Removing conference media listeners"); + + const { conferenceRef, conferenceHandlers } = state; + + conferenceRef.removeEventListener( + JitsiConferenceEvents.CONNECTION_INTERRUPTED, + conferenceHandlers.interruptedHandler + ); + conferenceRef.removeEventListener( + JitsiConferenceEvents.CONNECTION_RESTORED, + conferenceHandlers.restoredHandler + ); + conferenceRef.removeEventListener( + JitsiConferenceEvents.SUSPEND_DETECTED, + conferenceHandlers.suspendHandler + ); + + state.conferenceHandlers = undefined; + state.conferenceRef = undefined; + state.hasConferenceListeners = false; + console.log("[LISTENER_SETUP] Conference media listeners removed successfully"); }; /** @@ -52,18 +97,63 @@ export const setupConferenceMediaListeners = ( */ export const setupXMPPConnectionListeners = (connection: any, dispatch: IStore["dispatch"], state: ConnectionState) => { if (!connection || state.hasConnectionListeners) { + console.log("[LISTENER_SETUP] Skipping XMPP listeners setup", { + noConnection: !connection, + alreadyHasListeners: state.hasConnectionListeners + }); return; } - connection.addEventListener(JitsiConnectionEvents.CONNECTION_ESTABLISHED, () => handleXMPPConnected()); + console.log("[LISTENER_SETUP] Setting up XMPP connection listeners"); - connection.addEventListener(JitsiConnectionEvents.CONNECTION_DISCONNECTED, (message: string) => - handleXMPPDisconnected(dispatch, message) - ); + const connectedHandler = () => handleXMPPConnected(); + const disconnectedHandler = (message: string) => handleXMPPDisconnected(dispatch, message); + const failedHandler = (error: any, message: string) => handleXMPPConnectionFailed(dispatch, error, message); - connection.addEventListener(JitsiConnectionEvents.CONNECTION_FAILED, (error: any, message: string) => - handleXMPPConnectionFailed(dispatch, error, message) - ); + connection.addEventListener(JitsiConnectionEvents.CONNECTION_ESTABLISHED, connectedHandler); + connection.addEventListener(JitsiConnectionEvents.CONNECTION_DISCONNECTED, disconnectedHandler); + connection.addEventListener(JitsiConnectionEvents.CONNECTION_FAILED, failedHandler); + state.connectionHandlers = { + connectedHandler, + disconnectedHandler, + failedHandler + }; + state.connectionRef = connection; state.hasConnectionListeners = true; + console.log("[LISTENER_SETUP] XMPP connection listeners registered successfully"); +}; + +/** + * Removes event listeners for XMPP connection events + * + * @param state - Connection state containing handler references + */ +export const removeXMPPConnectionListeners = (state: ConnectionState) => { + if (!state.connectionRef || !state.connectionHandlers) { + console.log("[LISTENER_SETUP] Skipping XMPP listeners removal - no refs"); + return; + } + + console.log("[LISTENER_SETUP] Removing XMPP connection listeners"); + + const { connectionRef, connectionHandlers } = state; + + connectionRef.removeEventListener( + JitsiConnectionEvents.CONNECTION_ESTABLISHED, + connectionHandlers.connectedHandler + ); + connectionRef.removeEventListener( + JitsiConnectionEvents.CONNECTION_DISCONNECTED, + connectionHandlers.disconnectedHandler + ); + connectionRef.removeEventListener( + JitsiConnectionEvents.CONNECTION_FAILED, + connectionHandlers.failedHandler + ); + + state.connectionHandlers = undefined; + state.connectionRef = undefined; + state.hasConnectionListeners = false; + console.log("[LISTENER_SETUP] XMPP connection listeners removed successfully"); }; diff --git a/react/features/base/meet/middlewares/connection-stability/connection-notifications/types.ts b/react/features/base/meet/middlewares/connection-stability/connection-notifications/types.ts index 48a615197a3f..eca491eebab3 100644 --- a/react/features/base/meet/middlewares/connection-stability/connection-notifications/types.ts +++ b/react/features/base/meet/middlewares/connection-stability/connection-notifications/types.ts @@ -19,4 +19,34 @@ export interface ConnectionState { * Used to only show "connection restored" notification if there was a previous interruption */ wasMediaConnectionInterrupted: boolean; + + /** + * Stored handler references for conference media listeners + * Required for proper cleanup via removeEventListener + */ + conferenceHandlers?: { + interruptedHandler: () => void; + restoredHandler: () => void; + suspendHandler: () => void; + }; + + /** + * Stored handler references for XMPP connection listeners + * Required for proper cleanup via removeEventListener + */ + connectionHandlers?: { + connectedHandler: () => void; + disconnectedHandler: (message: string) => void; + failedHandler: (error: any, message: string) => void; + }; + + /** + * Reference to the conference object for listener removal + */ + conferenceRef?: any; + + /** + * Reference to the connection object for listener removal + */ + connectionRef?: any; } diff --git a/react/features/base/meet/middlewares/connection-stability/index.ts b/react/features/base/meet/middlewares/connection-stability/index.ts index bacad5c7140c..c260460526a7 100644 --- a/react/features/base/meet/middlewares/connection-stability/index.ts +++ b/react/features/base/meet/middlewares/connection-stability/index.ts @@ -13,7 +13,6 @@ import './connection-notifications'; import './middleware.datachannel'; import './middleware.error-handling'; import './middleware.poor-connection'; -import './middleware.auto-reconnect'; export { }; diff --git a/react/features/base/meet/middlewares/connection-stability/middleware.auto-reconnect.ts b/react/features/base/meet/middlewares/connection-stability/middleware.auto-reconnect.ts deleted file mode 100644 index 8f4bd01c70bf..000000000000 --- a/react/features/base/meet/middlewares/connection-stability/middleware.auto-reconnect.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { batch } from "react-redux"; -import { AnyAction } from "redux"; -import { IStore } from "../../../../app/types"; -import { hideNotification } from "../../../../notifications/actions"; -import { CONFERENCE_WILL_LEAVE } from "../../../conference/actionTypes"; -import { isLeavingConferenceManually, setLeaveConferenceManually } from "../../general/utils/conferenceState"; -import { CONNECTION_DISCONNECTED, CONNECTION_ESTABLISHED, CONNECTION_FAILED } from "../../../connection/actionTypes"; -import { connect } from "../../../connection/actions.web"; -import { setJWT } from "../../../jwt/actions"; -import MiddlewareRegistry from "../../../redux/MiddlewareRegistry"; -import { trackRemoved } from "../../../tracks/actions.any"; -import { hideLoader, showLoader } from "../../loader"; - -const RECONNECTION_NOTIFICATION_ID = "connection.reconnecting"; -const RECONNECTION_LOADER_ID = "auto-reconnect"; -const RECONNECTION_WAIT_TIME_MS = 15000; -const MAX_RECONNECTION_ATTEMPTS = 2; -const RECONNECTION_DELAY_MS = 3000; -const JWT_EXPIRED_ERROR = "connection.passwordRequired"; - -let reconnectionTimer: number | null = null; -let isReconnecting = false; -let reconnectionAttempts = 0; - -export const isAutoReconnecting = () => isReconnecting; - -const hideReconnectionNotification = (store: IStore) => { - store.dispatch(hideNotification(RECONNECTION_NOTIFICATION_ID)); -}; - -const showReconnectionLoader = (store: IStore, attempt: number) => { - const textKey = attempt <= MAX_RECONNECTION_ATTEMPTS ? "loader.reconnecting" : "loader.reloading"; - - store.dispatch(showLoader(undefined, textKey, RECONNECTION_LOADER_ID)); -}; - -const hideReconnectionLoader = (store: IStore) => { - store.dispatch(hideLoader(RECONNECTION_LOADER_ID)); -}; - -const reloadPage = () => { - window.location.reload(); -}; - -const clearExpiredJWT = (store: IStore) => { - store.dispatch(setJWT(undefined)); -}; - -const clearRemoteTracks = (store: IStore) => { - const state = store.getState(); - const remoteTracks = state["features/base/tracks"].filter((t) => !t.local); - - batch(() => { - for (const track of remoteTracks) { - store.dispatch(trackRemoved(track.jitsiTrack)); - } - }); -}; - -const triggerReconnection = (store: IStore) => { - store.dispatch(connect()); -}; - -const scheduleRetry = (store: IStore) => { - reconnectionTimer = window.setTimeout(() => { - if (!isLeavingConferenceManually() && isReconnecting) { - attemptReconnection(store); - } - }, RECONNECTION_DELAY_MS); -}; - -const handleMaxAttemptsReached = (store: IStore) => { - isReconnecting = true; - showReconnectionLoader(store, reconnectionAttempts + 1); - reconnectionTimer = window.setTimeout(reloadPage, 2000); -}; - -/** - * Attempts to reconnect by clearing JWT and connecting to conference again. - * If max attempts reached, reloads the page. - */ -const attemptReconnection = async (store: IStore) => { - if (isLeavingConferenceManually()) return; - - if (reconnectionAttempts >= MAX_RECONNECTION_ATTEMPTS) { - handleMaxAttemptsReached(store); - return; - } - - reconnectionAttempts++; - isReconnecting = true; - showReconnectionLoader(store, reconnectionAttempts); - - try { - clearRemoteTracks(store); - clearExpiredJWT(store); - await new Promise((resolve) => setTimeout(resolve, 100)); - triggerReconnection(store); - scheduleRetry(store); - } catch (error) { - console.error("[AUTO_RECONNECT] Reconnection error:", error); - scheduleRetry(store); - } -}; - -const clearTimer = () => { - if (reconnectionTimer !== null) { - clearTimeout(reconnectionTimer); - reconnectionTimer = null; - } -}; - -const resetReconnectionState = () => { - clearTimer(); - reconnectionAttempts = 0; - isReconnecting = false; -}; - -/** - * Middleware that handles automatic reconnection when JWT expires or connection is lost. - */ -MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: AnyAction) => { - const result = next(action); - - switch (action.type) { - case CONFERENCE_WILL_LEAVE: { - setLeaveConferenceManually(true); - resetReconnectionState(); - hideReconnectionNotification(store); - hideReconnectionLoader(store); - break; - } - - case CONNECTION_DISCONNECTED: { - if (isLeavingConferenceManually()) break; - - clearTimer(); - reconnectionAttempts = 0; - isReconnecting = true; - - reconnectionTimer = window.setTimeout(() => { - if (!isLeavingConferenceManually() && isReconnecting) { - attemptReconnection(store); - } - }, RECONNECTION_WAIT_TIME_MS); - - break; - } - - case CONNECTION_ESTABLISHED: { - if (isReconnecting) { - hideReconnectionNotification(store); - hideReconnectionLoader(store); - } - - resetReconnectionState(); - setLeaveConferenceManually(false); - break; - } - - case CONNECTION_FAILED: { - const { error } = action; - console.log("[AUTO_RECONNECT] Connection failed with error:", error); - if (error?.name === JWT_EXPIRED_ERROR && !isLeavingConferenceManually() && !isReconnecting) { - attemptReconnection(store); - } - - break; - } - } - - return result; -}); - -export default {}; diff --git a/react/features/base/meet/middlewares/connection-stability/middleware.poor-connection.ts b/react/features/base/meet/middlewares/connection-stability/middleware.poor-connection.ts index 5f8f0f395638..258ee95f46d1 100644 --- a/react/features/base/meet/middlewares/connection-stability/middleware.poor-connection.ts +++ b/react/features/base/meet/middlewares/connection-stability/middleware.poor-connection.ts @@ -15,6 +15,8 @@ let conferenceJoinTime: number | null = null; let lastWarningTime: number | null = null; let isNotificationCurrentlyShown = false; let isSubscribedToStats = false; +let subscribedParticipantId: string | null = null; +let subscribedCallback: ((stats: IConnectionStats) => void) | null = null; interface IConnectionStats { connectionQuality?: number; @@ -100,7 +102,10 @@ MiddlewareRegistry.register((store: IStore) => (next) => (action: AnyAction) => isNotificationCurrentlyShown = false; if (localParticipant.id && !isSubscribedToStats) { - statsEmitter.subscribeToClientStats(localParticipant.id, onStatsUpdated(store)); + const callback = onStatsUpdated(store); + subscribedCallback = callback; + subscribedParticipantId = localParticipant.id; + statsEmitter.subscribeToClientStats(localParticipant.id, callback); isSubscribedToStats = true; } @@ -110,9 +115,18 @@ MiddlewareRegistry.register((store: IStore) => (next) => (action: AnyAction) => case CONFERENCE_WILL_LEAVE: { // User manually hung up - hide notification and reset state hidePoorConnectionWarning(store); + + // Unsubscribe from stats emitter to prevent memory leaks + if (isSubscribedToStats && subscribedParticipantId && subscribedCallback) { + statsEmitter.unsubscribeToClientStats(subscribedParticipantId, subscribedCallback); + } + conferenceJoinTime = null; lastWarningTime = null; isNotificationCurrentlyShown = false; + isSubscribedToStats = false; + subscribedParticipantId = null; + subscribedCallback = null; break; } } diff --git a/react/features/e2ee/middleware.ts b/react/features/e2ee/middleware.ts index ac4e8760ba48..8722718c29dc 100644 --- a/react/features/e2ee/middleware.ts +++ b/react/features/e2ee/middleware.ts @@ -143,6 +143,14 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => { return next(action); }); +/** + * Stored E2EE event handler references for cleanup. + */ +let e2eeHandlerRefs: { + conference: any; + handlers: Map; +} | null = null; + /** * Set up state change listener to perform maintenance tasks when the conference * is left or failed. @@ -150,42 +158,74 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => { StateListenerRegistry.register( (state) => getCurrentConference(state), (conference, { dispatch }, previousConference) => { - if (previousConference) { - dispatch(toggleE2EE(false)); + if (previousConference && e2eeHandlerRefs?.conference === previousConference) { + console.log("[E2EE] Conference changed - cleaning up previous conference"); + + if (previousConference?.cleanUpWebWorkers) { + console.log("[E2EE] Calling cleanUpWebWorkers() to terminate E2EE worker"); + previousConference.cleanUpWebWorkers(); + } else { + console.warn("[E2EE] cleanUpWebWorkers() not available on previous conference"); + } + + if (e2eeHandlerRefs) { + console.log(`[E2EE] Removing ${e2eeHandlerRefs.handlers.size} event handlers`); + for (const [event, handler] of e2eeHandlerRefs.handlers) { + previousConference.off(event, handler); + } + } + + e2eeHandlerRefs = null; } if (conference) { - conference.on(JitsiConferenceEvents.E2EE_SAS_AVAILABLE, (sas: object) => { - if (ConfigService.instance.isDevelopment()) { - dispatch(openDialog('ParticipantVerificationDialog', ParticipantVerificationSASDialog, { sas })); - } - }); - - conference.on(JitsiConferenceEvents.E2EE_KEY_SYNC_FAILED, () => { - dispatch(showWarningNotification({ - titleKey: 'notify.encryptionKeySyncFailedTitle', - descriptionKey: 'notify.encryptionKeySyncFailed' - }, NOTIFICATION_TIMEOUT_TYPE.STICKY)); - }); - conference.on(JitsiConferenceEvents.E2EE_VERIFICATION_READY, (pId: string, sas: object) => { - // Added our ParticipantVerificationSASDialog - dispatch(openDialog("ParticipantVerificationDialog", ParticipantVerificationSASDialog, { pId, sas })); - }); - - conference.on(JitsiConferenceEvents.E2EE_CRYPTO_FAILED, () => { - logger.debug(`E2EE: crypto failure detected`); - dispatch(showWarningNotification({ - titleKey: 'notify.cryptoFailedTitle', - descriptionKey: 'notify.cryptoFailed' - }, NOTIFICATION_TIMEOUT_TYPE.STICKY)); - }); - - conference.on(JitsiConferenceEvents.E2EE_KEY_SYNC_AFTER_TIMEOUT, () => { - dispatch(showNotification({ - titleKey: 'notify.encryptionKeySyncRestoredTitle', - descriptionKey: 'notify.encryptionKeySyncRestored' - }, NOTIFICATION_TIMEOUT_TYPE.STICKY)); - }); + console.log("[E2EE] Setting up E2EE handlers for new conference"); + const handlers = new Map(); + + const sasAvailableHandler = (sas: object) => { + if (ConfigService.instance.isDevelopment()) { + dispatch(openDialog('ParticipantVerificationDialog', ParticipantVerificationSASDialog, { sas })); + } + }; + handlers.set(JitsiConferenceEvents.E2EE_SAS_AVAILABLE, sasAvailableHandler); + conference.on(JitsiConferenceEvents.E2EE_SAS_AVAILABLE, sasAvailableHandler); + + const keySyncFailedHandler = () => { + dispatch(showWarningNotification({ + titleKey: 'notify.encryptionKeySyncFailedTitle', + descriptionKey: 'notify.encryptionKeySyncFailed' + }, NOTIFICATION_TIMEOUT_TYPE.STICKY)); + }; + handlers.set(JitsiConferenceEvents.E2EE_KEY_SYNC_FAILED, keySyncFailedHandler); + conference.on(JitsiConferenceEvents.E2EE_KEY_SYNC_FAILED, keySyncFailedHandler); + + const verificationReadyHandler = (pId: string, sas: object) => { + dispatch(openDialog('ParticipantVerificationDialog', ParticipantVerificationSASDialog, { pId, sas })); + }; + handlers.set(JitsiConferenceEvents.E2EE_VERIFICATION_READY, verificationReadyHandler); + conference.on(JitsiConferenceEvents.E2EE_VERIFICATION_READY, verificationReadyHandler); + + const cryptoFailedHandler = () => { + dispatch(showWarningNotification({ + titleKey: 'notify.cryptoFailedTitle', + descriptionKey: 'notify.cryptoFailed' + }, NOTIFICATION_TIMEOUT_TYPE.STICKY)); + }; + handlers.set(JitsiConferenceEvents.E2EE_CRYPTO_FAILED, cryptoFailedHandler); + conference.on(JitsiConferenceEvents.E2EE_CRYPTO_FAILED, cryptoFailedHandler); + + const keySyncAfterTimeoutHandler = () => { + dispatch(showNotification({ + titleKey: 'notify.encryptionKeySyncRestoredTitle', + descriptionKey: 'notify.encryptionKeySyncRestored' + }, NOTIFICATION_TIMEOUT_TYPE.STICKY)); + }; + handlers.set(JitsiConferenceEvents.E2EE_KEY_SYNC_AFTER_TIMEOUT, keySyncAfterTimeoutHandler); + conference.on(JitsiConferenceEvents.E2EE_KEY_SYNC_AFTER_TIMEOUT, keySyncAfterTimeoutHandler); + + // Store references for cleanup + e2eeHandlerRefs = { conference, handlers }; + console.log(`[E2EE] Successfully registered ${handlers.size} E2EE event handlers`); } } ); diff --git a/yarn.lock b/yarn.lock index ce266a416176..b3b32f055029 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11081,9 +11081,9 @@ levn@^0.4.1: prelude-ls "^1.2.1" type-check "~0.4.0" -"lib-jitsi-meet@https://github.com/internxt/lib-jitsi-meet/releases/download/v.0.0.20/lib-jitsi-meet-0.0.20.tgz": - version "0.0.20" - resolved "https://github.com/internxt/lib-jitsi-meet/releases/download/v.0.0.20/lib-jitsi-meet-0.0.20.tgz#5048eba36fa1f6b1884c00d6d5a21e847c9d2c46" +"lib-jitsi-meet@https://github.com/internxt/lib-jitsi-meet/releases/download/v.0.0.20-debug/lib-jitsi-meet-0.0.20-debug.tgz": + version "0.0.20-debug" + resolved "https://github.com/internxt/lib-jitsi-meet/releases/download/v.0.0.20-debug/lib-jitsi-meet-0.0.20-debug.tgz#3e5a6b196a215d2dc449c588c23ad32bded2b1de" dependencies: "@hexagon/base64" "^2.0.4" "@jitsi/js-utils" "^2.6.7"