Compare commits

...

10 Commits

11 changed files with 161 additions and 107 deletions

10
package-lock.json generated
View File

@@ -66,7 +66,7 @@
"js-md5": "0.6.1",
"js-sha512": "0.8.0",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2089.0.0+75c1c6ff/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2091.0.0+518cec5f/lib-jitsi-meet.tgz",
"lodash-es": "4.17.21",
"null-loader": "4.0.1",
"optional-require": "1.0.3",
@@ -18260,8 +18260,8 @@
},
"node_modules/lib-jitsi-meet": {
"version": "0.0.0",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2089.0.0+75c1c6ff/lib-jitsi-meet.tgz",
"integrity": "sha512-1sd9+YztXYhJ5mI5fksr+EHDqEVZZYz1oIe3Uv+QIicABWL06kwFc4lTRdBUSrYf+GRXZ9F37UQjxuOOh1lu8A==",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2091.0.0+518cec5f/lib-jitsi-meet.tgz",
"integrity": "sha512-mmdvU3B7dYWV6RcfFW/BWJkaNR3LLCG3wHcBYT8WZKVbh00vu4SZJKwtjiVrw2aIP1FcXoD3T0RJ5+IaGROU3A==",
"license": "Apache-2.0",
"dependencies": {
"@jitsi/js-utils": "2.4.6",
@@ -39716,8 +39716,8 @@
}
},
"lib-jitsi-meet": {
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2089.0.0+75c1c6ff/lib-jitsi-meet.tgz",
"integrity": "sha512-1sd9+YztXYhJ5mI5fksr+EHDqEVZZYz1oIe3Uv+QIicABWL06kwFc4lTRdBUSrYf+GRXZ9F37UQjxuOOh1lu8A==",
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2091.0.0+518cec5f/lib-jitsi-meet.tgz",
"integrity": "sha512-mmdvU3B7dYWV6RcfFW/BWJkaNR3LLCG3wHcBYT8WZKVbh00vu4SZJKwtjiVrw2aIP1FcXoD3T0RJ5+IaGROU3A==",
"requires": {
"@jitsi/js-utils": "2.4.6",
"@jitsi/logger": "2.1.1",

View File

@@ -72,7 +72,7 @@
"js-md5": "0.6.1",
"js-sha512": "0.8.0",
"jwt-decode": "2.2.0",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2089.0.0+75c1c6ff/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v2091.0.0+518cec5f/lib-jitsi-meet.tgz",
"lodash-es": "4.17.21",
"null-loader": "4.0.1",
"optional-require": "1.0.3",

View File

@@ -26,7 +26,7 @@ import { AudioMixerEffect } from '../../stream-effects/audio-mixer/AudioMixerEff
import { iAmVisitor } from '../../visitors/functions';
import { overwriteConfig } from '../config/actions';
import { CONNECTION_ESTABLISHED, CONNECTION_FAILED, CONNECTION_WILL_CONNECT } from '../connection/actionTypes';
import { connectionDisconnected, disconnect } from '../connection/actions';
import { connect, connectionDisconnected, disconnect, setPreferVisitor } from '../connection/actions';
import { validateJwt } from '../jwt/functions';
import { JitsiConferenceErrors, JitsiConferenceEvents, JitsiConnectionErrors } from '../lib-jitsi-meet';
import { MEDIA_TYPE } from '../media/constants';
@@ -78,6 +78,11 @@ import { IConferenceMetadata } from './reducer';
*/
let beforeUnloadHandler: ((e?: any) => void) | undefined;
/**
* A simple flag to avoid retrying more than once to join as a visitor when hitting max occupants reached.
*/
let retryAsVisitorOnMaxError = true;
/**
* Implements the middleware of the feature base/conference.
*
@@ -202,11 +207,20 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
break;
}
case JitsiConferenceErrors.CONFERENCE_MAX_USERS: {
dispatch(showErrorNotification({
hideErrorSupportLink: true,
descriptionKey: 'dialog.maxUsersLimitReached',
titleKey: 'dialog.maxUsersLimitReachedTitle'
}));
let retryAsVisitor = false;
if (error.params?.length && error.params[0]?.visitorsSupported) {
// visitors are supported, so let's try joining that way
retryAsVisitor = true;
}
if (!retryAsVisitor) {
dispatch(showErrorNotification({
hideErrorSupportLink: true,
descriptionKey: 'dialog.maxUsersLimitReached',
titleKey: 'dialog.maxUsersLimitReachedTitle'
}));
}
// In case of max users(it can be from a visitor node), let's restore
// oldConfig if any as we will be back to the main prosody.
@@ -220,6 +234,18 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
.then(() => dispatch(disconnect()));
}
if (retryAsVisitor && !newConfig && retryAsVisitorOnMaxError) {
retryAsVisitorOnMaxError = false;
logger.info('On max user reached will retry joining as a visitor');
dispatch(disconnect(true)).then(() => {
dispatch(setPreferVisitor(true));
return dispatch(connect());
});
}
break;
}
case JitsiConferenceErrors.NOT_ALLOWED_ERROR: {
@@ -300,6 +326,8 @@ function _conferenceJoined({ dispatch, getState }: IStore, next: Function, actio
requireDisplayName
} = getState()['features/base/config'];
retryAsVisitorOnMaxError = true;
dispatch(removeLobbyChatParticipant(true));
pendingSubjectChange && dispatch(setSubject(pendingSubjectChange));

View File

@@ -149,7 +149,7 @@ const Notification = ({
const titleText = title || (titleKey && t(titleKey, titleArguments));
const descriptionArray = _getDescription();
if (descriptionArray?.length) {
if (descriptionArray?.length && titleText) {
return (
<>
<Text
@@ -168,15 +168,29 @@ const Notification = ({
}
</>
);
} else if (descriptionArray?.length && !titleText) {
return (
<>
{
descriptionArray.map((line, index) => (
<Text
key = { index }
style = { styles.contentTextDescription }>
{ line.length >= CHAR_LIMIT ? line : replaceNonUnicodeEmojis(line) }
</Text>
))
}
</>
);
} else {
return (
<Text
numberOfLines = { 1 }
style = { styles.contentTextTitle as TextStyle }>
{ titleText }
</Text>
);
}
return (
<Text
numberOfLines = { 1 }
style = { styles.contentTextTitle as TextStyle }>
{ titleText }
</Text>
);
};
return (

View File

@@ -49,6 +49,12 @@ export default {
paddingTop: BaseTheme.spacing[1]
},
contentTextDescription: {
color: BaseTheme.palette.text04,
paddingLeft: BaseTheme.spacing[4],
paddingTop: BaseTheme.spacing[2]
},
contentTextTitleDescription: {
color: BaseTheme.palette.text04,
fontWeight: 'bold',

View File

@@ -72,7 +72,7 @@ function filter_stanza_in(stanza, session)
local nick_element = stanza:get_child('nick', NICK_NS);
if nick_element:get_text() ~= session.jitsi_meet_context_user.name then
if nick_element and nick_element:get_text() ~= session.jitsi_meet_context_user.name then
stanza:remove_children('nick', NICK_NS);
stanza:tag('nick', { xmlns = NICK_NS }):text(session.jitsi_meet_context_user.name):up();
end

View File

@@ -43,7 +43,7 @@ local function check_for_max_occupants(event)
-- If there is no whitelist, just check the count.
if not whitelist and count >= slots then
module:log("info", "Attempt to enter a maxed out MUC");
module:log("info", "Attempt to enter a maxed out room: %s", room.jid);
origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
return true;
end
@@ -60,7 +60,7 @@ local function check_for_max_occupants(event)
-- If the room is full (<0 slots left), error out.
if slots <= 0 then
module:log("info", "Attempt to enter a maxed out MUC");
module:log("info", "Attempt to enter a maxed out room:%s", room.jid);
origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
return true;
end

View File

@@ -216,6 +216,10 @@ async function joinParticipant( // eslint-disable-line max-params
tenant = config.iframe.tenant;
}
if (!tenant && ctx.testProperties.useIFrameApi) {
tenant = config.iframe.tenant;
}
return await newParticipant.joinConference({
...options,
tenant: tenant,

View File

@@ -1,81 +0,0 @@
import { expect } from '@wdio/globals';
import { setTestProperties } from '../../helpers/TestProperties';
import { ensureOneParticipant, ensureTwoParticipants } from '../../helpers/participants';
setTestProperties(__filename, {
useIFrameApi: true,
useWebhookProxy: true,
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Visitors', () => {
it('joining the meeting', async () => {
const { webhooksProxy } = ctx;
if (webhooksProxy) {
webhooksProxy.defaultMeetingSettings = {
visitorsEnabled: true,
visitorsLive: false
};
}
await ensureOneParticipant();
const { p1 } = ctx;
if (await p1.execute(() => config.disableIframeAPI)) {
// skip the test if iframeAPI is disabled or visitors are not supported
ctx.skipSuiteTests = true;
return;
}
await p1.driver.waitUntil(() => p1.execute(() => APP.conference._room.isVisitorsSupported()), {
timeout: 2000
}).then(async () => {
await p1.switchToMainFrame();
}).catch(() => {
ctx.skipSuiteTests = true;
});
});
it('go live', async () => {
await ensureTwoParticipants({
preferGenerateToken: true,
tokenOptions: { visitor: true },
skipWaitToJoin: true,
skipInMeetingChecks: true
});
const { p1, p2 } = ctx;
const p2Visitors = p2.getVisitors();
const p1Visitors = p1.getVisitors();
await p2.driver.waitUntil(async () => p2Visitors.isVisitorsQueueUIShown(), {
timeout: 5000,
timeoutMsg: 'Missing visitors queue UI'
});
await p1.driver.waitUntil(async () => await p1Visitors.getWaitingVisitorsInQueue()
=== 'Viewers waiting in queue: 1', {
timeout: 15000,
timeoutMsg: 'Missing visitors queue count in UI'
});
await p1Visitors.goLive();
await p2.waitToJoinMUC();
await p2.waitForReceiveMedia(15000, 'Visitor is not receiving media');
await p2.waitForRemoteStreams(1);
await p2.driver.waitUntil(() => p2Visitors.hasVisitorsDialog(), {
timeout: 5000,
timeoutMsg: 'Missing visitors dialog'
});
expect((await p1Visitors.getVisitorsCount()).trim()).toBe('1');
expect((await p1Visitors.getVisitorsHeaderFromParticipantsPane()).trim()).toBe('Viewers 1');
});
});

View File

@@ -0,0 +1,74 @@
import { expect } from '@wdio/globals';
import { Participant } from '../../../helpers/Participant';
import { setTestProperties } from '../../../helpers/TestProperties';
import { joinMuc, generateJaasToken as t } from '../../helpers/jaas';
setTestProperties(__filename, {
useJaas: true,
useWebhookProxy: true,
usesBrowsers: [ 'p1', 'p2' ]
});
describe('Visitors', () => {
let visitor: Participant, moderator: Participant;
it('setup', async () => {
ctx.webhooksProxy.defaultMeetingSettings = {
visitorsEnabled: true,
visitorsLive: false
};
moderator = await joinMuc({
name: 'p1',
token: t({ room: ctx.roomName, displayName: 'Mo de Rator', moderator: true })
});
// TODO: Remove this in favor of configurable test expectations
await moderator.driver.waitUntil(() => moderator.execute(() => APP.conference._room.isVisitorsSupported()), {
timeout: 2000
}).catch(e => {
console.log(`Skipping test due to error: ${e}`);
ctx.skipSuiteTests = true;
});
visitor = await joinMuc({
name: 'p2',
token: t({ room: ctx.roomName, displayName: 'Visi Tor', visitor: true })
}, {
skipWaitToJoin: true
});
});
it('go live', async () => {
const vVisitors = visitor.getVisitors();
const mVisitors = moderator.getVisitors();
await visitor.driver.waitUntil(async () => vVisitors.isVisitorsQueueUIShown(), {
timeout: 5000,
timeoutMsg: 'Missing visitors queue UI'
});
await moderator.driver.waitUntil(async () => await mVisitors.getWaitingVisitorsInQueue()
=== 'Viewers waiting in queue: 1', {
timeout: 15000,
timeoutMsg: 'Missing visitors queue count in UI'
});
await mVisitors.goLive();
await visitor.waitToJoinMUC();
await visitor.waitForReceiveMedia(15000, 'Visitor is not receiving media');
await visitor.waitForRemoteStreams(1);
await visitor.driver.waitUntil(() => vVisitors.hasVisitorsDialog(), {
timeout: 5000,
timeoutMsg: 'Missing visitors dialog'
});
expect((await mVisitors.getVisitorsCount()).trim()).toBe('1');
expect((await mVisitors.getVisitorsHeaderFromParticipantsPane()).trim()).toBe('Viewers 1');
});
});

View File

@@ -245,8 +245,17 @@ export const config: WebdriverIO.MultiremoteConfig = {
// If we are running the iFrameApi tests, we need to mark it as such and if needed to create the proxy
// and connect to it.
if (testProperties.useWebhookProxy && testsConfig.webhooksProxy.enabled && !globalAny.ctx.webhooksProxy) {
// Note this prevents iframe and jaas test from running together.
const tenant = testsConfig.jaas.enabled ? testsConfig.jaas.tenant : testsConfig.iframe.tenant;
let tenant = testsConfig.jaas.tenant;
if (!testProperties.useJaas) {
tenant = testsConfig.iframe.tenant;
}
if (!tenant) {
console.log(`Can not configure WebhookProxy, missing tenant in config. Skipping ${testName}.`);
globalAny.ctx.skipSuiteTests = true;
return;
}
globalAny.ctx.webhooksProxy = new WebhookProxy(
`${testsConfig.webhooksProxy.url}?tenant=${tenant}&room=${globalAny.ctx.roomName}`,