fix: GUM prompt not displayed after deeplinking page.

When we open a custom scheme URL before the window load event has been fired it seems that GUM prompt is not displayed after this due to Chrome bug. See more details here https://issues.chromium.org/issues/41398687.

The result in Jitsi Meet is the following:
If the user is joining a call for first time and haven't granted A/V permissions and lands on the deeplinking page we try to open the desktop app via redirect to a custom scheme URL. If the user chooses cancel and "Launch in web" we go to the prejoin screen and proceed with the initial GUM. At this point any GUM call won't display the permission prompt due to the browser bug and will go on forever making it impossible for the user to unmute camera or microphone.
This commit is contained in:
Hristo Terezov
2025-07-09 14:48:21 -05:00
parent 0db9f1b7b4
commit 5f5fa5a2f5
4 changed files with 51 additions and 1 deletions

View File

@@ -5,6 +5,7 @@ import { toState } from '../base/redux/functions';
import { getServerURL } from '../base/settings/functions.native';
export * from './functions.any';
import logger from './logger';
/**
* Retrieves the default URL for the app. This can either come from a prop to
@@ -38,3 +39,18 @@ export function getName() {
export function getSdkBundlePath() {
return NativeModules.AppInfo.sdkBundlePath;
}
/**
* This is a dummy implementation for compatibility with web. It executes the passed handler right away.
*
* @param {Function} handler - The callback function to execute.
* @returns {void}
*/
export function executeAfterLoad(handler: () => void) {
try {
handler();
} catch (error) {
logger.error('Error executing handler after load:', error);
}
}

View File

@@ -1,8 +1,10 @@
import { IStateful } from '../base/app/types';
import { toState } from '../base/redux/functions';
import { getServerURL } from '../base/settings/functions.web';
import { getJitsiMeetGlobalNS } from '../base/util/helpers';
export * from './functions.any';
import logger from './logger';
/**
* Retrieves the default URL for the app. This can either come from a prop to
@@ -31,3 +33,27 @@ export function getDefaultURL(stateful: IStateful) {
export function getName() {
return interfaceConfig.APP_NAME;
}
/**
* Executes a handler function after the window load event has been received.
* If the app has already loaded, the handler is executed immediately.
* Otherwise, the handler is registered as a 'load' event listener.
*
* @param {Function} handler - The callback function to execute.
* @returns {void}
*/
export function executeAfterLoad(handler: () => void) {
const safeHandler = () => {
try {
handler();
} catch (error) {
logger.error('Error executing handler after load:', error);
}
};
if (getJitsiMeetGlobalNS()?.hasLoaded) {
safeHandler();
} else {
window.addEventListener('load', safeHandler);
}
}

View File

@@ -1,3 +1,4 @@
import { executeAfterLoad } from '../app/functions';
import { IReduxState } from '../app/types';
import { URI_PROTOCOL_PATTERN } from '../base/util/uri';
@@ -16,7 +17,10 @@ export function _openDesktopApp(_state: Object) {
const { appScheme } = deeplinkingDesktop;
const regex = new RegExp(URI_PROTOCOL_PATTERN, 'gi');
window.location.href = window.location.href.replace(regex, `${appScheme}:`);
// This is needed to workaround https://issues.chromium.org/issues/41398687
executeAfterLoad(() => {
window.location.href = window.location.href.replace(regex, `${appScheme}:`);
});
return Promise.resolve(true);
}

View File

@@ -47,11 +47,15 @@ if (Platform.OS === 'ios') {
const globalNS = getJitsiMeetGlobalNS();
const connectionTimes = getJitsiMeetGlobalNSConnectionTimes();
// Used to check if the load event has been fired.
globalNS.hasLoaded = false;
// Used for automated performance tests.
connectionTimes['index.loaded'] = window.indexLoadedTime;
window.addEventListener('load', () => {
connectionTimes['window.loaded'] = window.loadedEventTime;
globalNS.hasLoaded = true;
});
document.addEventListener('DOMContentLoaded', () => {