Compare commits

..

11 Commits

Author SHA1 Message Date
Aaron van Meerten
a49b6140e0 feature: empty token verification allow list 2022-12-15 09:02:48 -06:00
Saúl Ibarra Corretgé
c30d1e7479 fix(rn,dynamic-branding) fix handling bogus response from the backend 2022-12-15 12:19:17 +01:00
Avram Tudor
cbbe58a1ec fix(prejoin) fix prejoin app dialogs not being visible (#12689) 2022-12-15 11:29:08 +02:00
Robert Pintilii
aef5328aeb ref(TS) Convert some features to TS (#12651) 2022-12-14 14:23:07 +02:00
Gabriel Borlea
f5ac1b6271 chore(deps) lib-jitsi-meet@latest (#12684)
https://github.com/jitsi/lib-jitsi-meet/compare/v1556.0.0+d9ee8f59...v1557.0.0+8df33524
2022-12-14 12:12:45 +02:00
Titus Moldovan
ca9f0a6788 fix(rn,screenshare) rework the logic for unified-plan with multi-stream 2022-12-14 08:35:33 +01:00
Saúl Ibarra Corretgé
e7c5ae5936 fix(rn,config) don't disable multi-stream sending on RN
We don't plan to send both (camera and screen) streams at once, but we
do plan to align with web and have both streams at the same time, with
the camera being muted when we start screen sharing.
2022-12-14 08:35:33 +01:00
Saúl Ibarra Corretgé
c43a319576 feat(deps,rn) update react-native-webrtc@106
Brings in Unified Plan support!
2022-12-14 08:35:33 +01:00
Saúl Ibarra Corretgé
585c9aa0d2 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1554.0.0+a25023e7...v1556.0.0+d9ee8f59
2022-12-14 08:34:36 +01:00
Horatiu Muresan
768f10d966 feat(route) Redirect to a custom welcome page 2022-12-13 22:04:11 +02:00
Horatiu Muresan
704740969b fix(prejoin) Fix missing lib error 2022-12-13 22:04:11 +02:00
46 changed files with 366 additions and 284 deletions

View File

@@ -580,9 +580,19 @@ var config = {
// Require users to always specify a display name.
// requireDisplayName: true,
// DEPRECATED! Use 'welcomePage.disabled' instead.
// Whether to use a welcome page or not. In case it's false a random room
// will be joined when no room is specified.
enableWelcomePage: true,
// enableWelcomePage: true,
// Configs for welcome page.
// welcomePage: {
// // Whether to disable welcome page. In case it's disabled a random room
// // will be joined when no room is specified.
// disabled: false,
// // If set,landing page will redirect to this URL.
// customUrl: ''
// },
// Disable app shortcuts that are registered upon joining a conference
// disableShortcuts: false,

View File

@@ -2,7 +2,8 @@ $sidePanelWidth: 300px;
.prejoin-third-party {
flex-direction: column-reverse;
z-index: auto;
.content {
height: auto;
margin: 0 auto;

1
globals.d.ts vendored
View File

@@ -24,6 +24,7 @@ declare global {
JITSI_MEET_LITE_SDK?: boolean;
interfaceConfig?: any;
JitsiMeetJS?: any;
JitsiMeetElectron?: any;
}
const config: IConfig;

View File

@@ -385,7 +385,7 @@ PODS:
- react-native-video/Video (6.0.0-alpha.1):
- PromisesSwift
- React-Core
- react-native-webrtc (1.106.1):
- react-native-webrtc (106.0.0):
- JitsiWebRTC (~> 106.0.0)
- React-Core
- react-native-webview (11.15.1):
@@ -755,7 +755,7 @@ SPEC CHECKSUMS:
react-native-slider: 6e9b86e76cce4b9e35b3403193a6432ed07e0c81
react-native-splash-screen: 4312f786b13a81b5169ef346d76d33bc0c6dc457
react-native-video: bb6f12a7198db53b261fefb5d609dc77417acc8b
react-native-webrtc: 4a4c31be61f88d1d3356526eebce72f462a6760e
react-native-webrtc: 0a407105bf428c9157f2e8d4d6f7c844dc185933
react-native-webview: ea4899a1056c782afa96dd082179a66cbebf5504
React-perflogger: 0458a87ea9a7342079e7a31b0d32b3734fb8415f
React-RCTActionSheet: 22538001ea2926dea001111dd2846c13a0730bc9

26
package-lock.json generated
View File

@@ -74,7 +74,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/v1554.0.0+a25023e7/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1557.0.0+8df33524/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.4",
"moment-duration-format": "2.2.2",
@@ -114,7 +114,7 @@
"react-native-url-polyfill": "1.3.0",
"react-native-video": "https://git@github.com/react-native-video/react-native-video#7c48ae7c8544b2b537fb60194e9620b9fcceae52",
"react-native-watch-connectivity": "1.0.11",
"react-native-webrtc": "1.106.1",
"react-native-webrtc": "106.0.0",
"react-native-webview": "11.15.1",
"react-native-youtube-iframe": "2.2.1",
"react-redux": "7.1.0",
@@ -13497,8 +13497,8 @@
},
"node_modules/lib-jitsi-meet": {
"version": "0.0.0",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1554.0.0+a25023e7/lib-jitsi-meet.tgz",
"integrity": "sha512-dGEps2d9APyOMcCraVszB7h2KotBr9Q3Wzjr+8qt2EETXAk8y/naLSFJq19WINeqlvE5m+339cAVY0OoL5q3Gg==",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1557.0.0+8df33524/lib-jitsi-meet.tgz",
"integrity": "sha512-LH24V3aCAyNxrXkYsr4Syz9G+hDyfGo7JUu3suEbh1bS5Y4w8mwzTBHyGazLnD7/NDG5F783DY19/yCzR7stSQ==",
"license": "Apache-2.0",
"dependencies": {
"@jitsi/js-utils": "2.0.0",
@@ -16501,13 +16501,14 @@
}
},
"node_modules/react-native-webrtc": {
"version": "1.106.1",
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-1.106.1.tgz",
"integrity": "sha512-955gqWFdISARz9D4hmnPzKQwpaU+AGqUbU+vBjzLCozUseSJ69tTQg2cShyPCBH6A1rwJQE+mrdjcpkeGbx3pQ==",
"version": "106.0.0",
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-106.0.0.tgz",
"integrity": "sha512-nFl8WSNGMNxuIiaNAiJvILRcEC65yRxPOWTexLrM+vo44syt/4chEvzN9eOqXiPsOmsECQmwZupCUGR5XABUNg==",
"hasInstallScript": true,
"dependencies": {
"adm-zip": "0.5.9",
"base64-js": "1.5.1",
"debug": "4.3.4",
"event-target-shim": "6.0.2",
"tar": "6.1.11"
},
@@ -30495,8 +30496,8 @@
}
},
"lib-jitsi-meet": {
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1554.0.0+a25023e7/lib-jitsi-meet.tgz",
"integrity": "sha512-dGEps2d9APyOMcCraVszB7h2KotBr9Q3Wzjr+8qt2EETXAk8y/naLSFJq19WINeqlvE5m+339cAVY0OoL5q3Gg==",
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1557.0.0+8df33524/lib-jitsi-meet.tgz",
"integrity": "sha512-LH24V3aCAyNxrXkYsr4Syz9G+hDyfGo7JUu3suEbh1bS5Y4w8mwzTBHyGazLnD7/NDG5F783DY19/yCzR7stSQ==",
"requires": {
"@jitsi/js-utils": "2.0.0",
"@jitsi/logger": "2.0.0",
@@ -32789,12 +32790,13 @@
}
},
"react-native-webrtc": {
"version": "1.106.1",
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-1.106.1.tgz",
"integrity": "sha512-955gqWFdISARz9D4hmnPzKQwpaU+AGqUbU+vBjzLCozUseSJ69tTQg2cShyPCBH6A1rwJQE+mrdjcpkeGbx3pQ==",
"version": "106.0.0",
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-106.0.0.tgz",
"integrity": "sha512-nFl8WSNGMNxuIiaNAiJvILRcEC65yRxPOWTexLrM+vo44syt/4chEvzN9eOqXiPsOmsECQmwZupCUGR5XABUNg==",
"requires": {
"adm-zip": "0.5.9",
"base64-js": "1.5.1",
"debug": "4.3.4",
"event-target-shim": "6.0.2",
"tar": "6.1.11"
},

View File

@@ -79,7 +79,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/v1554.0.0+a25023e7/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1557.0.0+8df33524/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.4",
"moment-duration-format": "2.2.2",
@@ -119,7 +119,7 @@
"react-native-url-polyfill": "1.3.0",
"react-native-video": "https://git@github.com/react-native-video/react-native-video#7c48ae7c8544b2b537fb60194e9620b9fcceae52",
"react-native-watch-connectivity": "1.0.11",
"react-native-webrtc": "1.106.1",
"react-native-webrtc": "106.0.0",
"react-native-webview": "11.15.1",
"react-native-youtube-iframe": "2.2.1",
"react-redux": "7.1.0",

View File

@@ -21,6 +21,7 @@ import { isVpaasMeeting } from '../jaas/functions';
import { clearNotifications, showNotification } from '../notifications/actions';
import { NOTIFICATION_TIMEOUT_TYPE } from '../notifications/constants';
import { setFatalError } from '../overlay/actions';
import { isWelcomePageEnabled } from '../welcome/functions';
import {
redirectToStaticPage,
@@ -203,7 +204,7 @@ export function maybeRedirectToWelcomePage(options: { feedbackSubmitted?: boolea
// if Welcome page is enabled redirect to welcome page after 3 sec, if
// there is a thank you message to be shown, 0.5s otherwise.
if (getState()['features/base/config'].enableWelcomePage) {
if (isWelcomePageEnabled(getState())) {
setTimeout(
() => {
dispatch(redirectWithStoredParams('/'));

View File

@@ -8,7 +8,7 @@ import { Conference } from '../conference';
import { getDeepLinkingPage } from '../deep-linking';
import { UnsupportedDesktopBrowser } from '../unsupported-browser';
import { BlankPage, WelcomePage } from '../welcome';
import { isWelcomePageEnabled } from '../welcome/functions';
import { getCustomLandingPageURL, isWelcomePageEnabled } from '../welcome/functions';
/**
* Determines which route is to be rendered in order to depict a specific Redux
@@ -75,7 +75,13 @@ function _getWebWelcomePageRoute(state) {
if (isWelcomePageEnabled(state)) {
if (isSupportedBrowser()) {
route.component = WelcomePage;
const customLandingPage = getCustomLandingPageURL(state);
if (customLandingPage) {
route.href = customLandingPage;
} else {
route.component = WelcomePage;
}
} else {
route.component = UnsupportedDesktopBrowser;
}

View File

@@ -46,6 +46,7 @@ export interface IJitsiConference {
avModerationApprove: Function;
avModerationReject: Function;
createVideoSIPGWSession: Function;
dial: Function;
disableAVModeration: Function;
enableAVModeration: Function;
end: Function;
@@ -74,6 +75,7 @@ export interface IJitsiConference {
myUserId: Function;
off: Function;
on: Function;
options: any;
removeTrack: Function;
replaceTrack: Function;
room: IJitsiConferenceRoom;

View File

@@ -127,6 +127,7 @@ export interface IConfig {
preventExecution: boolean;
}>;
callDisplayName?: string;
callFlowsEnabled?: boolean;
callStatsConfigParams?: {
additionalIDs?: {
customerID?: string;
@@ -344,6 +345,8 @@ export interface IConfig {
iAmRecorder?: boolean;
iAmSipGateway?: boolean;
inviteAppName?: string | null;
inviteServiceCallFlowsUrl?: string;
inviteServiceUrl?: string;
jaasActuatorUrl?: string;
jaasFeedbackMetadataURL?: string;
jaasTokenUrl?: string;
@@ -505,6 +508,10 @@ export interface IConfig {
webrtcIceUdpDisable?: boolean;
websocket?: string;
websocketKeepAliveUrl?: string;
welcomePage?: {
customUrl?: string;
disabled?: boolean;
};
whiteboard?: {
collabServerBaseUrl?: string;
enabled?: boolean;

View File

@@ -60,7 +60,7 @@ export function getMeetingRegion(state: IReduxState) {
* @returns {boolean}
*/
export function getMultipleVideoSendingSupportFeatureFlag(state: IReduxState) {
return navigator.product !== 'ReactNative' && isUnifiedPlanEnabled(state);
return isUnifiedPlanEnabled(state);
}
/**

View File

@@ -338,6 +338,13 @@ function _translateLegacyConfig(oldValue: IConfig) {
});
}
newValue.welcomePage = oldValue.welcomePage || {};
if (oldValue.hasOwnProperty('enableWelcomePage')
&& !newValue.welcomePage.hasOwnProperty('disabled')
) {
newValue.welcomePage.disabled = !oldValue.enableWelcomePage;
}
newValue.prejoinConfig = oldValue.prejoinConfig || {};
if (oldValue.hasOwnProperty('prejoinPageEnabled')
&& !newValue.prejoinConfig.hasOwnProperty('enabled')

View File

@@ -43,7 +43,8 @@ export const VIDEO_MUTISM_AUTHORITY = {
AUDIO_ONLY: 1 << 0,
BACKGROUND: 1 << 1,
USER: 1 << 2,
CAR_MODE: 1 << 3
CAR_MODE: 1 << 3,
SCREEN_SHARE: 1 << 4
};
/* eslint-enable no-bitwise */

View File

@@ -4,9 +4,17 @@ import { IReduxState, IStore } from '../../app/types';
import { setPictureInPictureEnabled } from '../../mobile/picture-in-picture/functions';
import { setAudioOnly } from '../audio-only/actions';
import JitsiMeetJS from '../lib-jitsi-meet';
import {
setScreenshareMuted,
setVideoMuted
} from '../media/actions';
import {
MEDIA_TYPE,
VIDEO_MUTISM_AUTHORITY
} from '../media/constants';
import { destroyLocalDesktopTrackIfExists, replaceLocalTrack } from './actions.any';
import { getLocalVideoTrack, isLocalVideoTrackDesktop } from './functions';
import { addLocalTrack, replaceLocalTrack } from './actions.any';
import { getLocalDesktopTrack, getTrackState, isLocalVideoTrackDesktop } from './functions.native';
export * from './actions.any';
@@ -31,7 +39,8 @@ export function toggleScreensharing(enabled: boolean, _ignore1?: boolean, _ignor
_startScreenSharing(dispatch, state);
}
} else {
dispatch(destroyLocalDesktopTrackIfExists());
dispatch(setScreenshareMuted(true));
dispatch(setVideoMuted(false, MEDIA_TYPE.VIDEO, VIDEO_MUTISM_AUTHORITY.SCREEN_SHARE));
setPictureInPictureEnabled(true);
}
};
@@ -47,26 +56,33 @@ export function toggleScreensharing(enabled: boolean, _ignore1?: boolean, _ignor
* @param {Object} state - The redux state.
* @returns {void}
*/
function _startScreenSharing(dispatch: Function, state: IReduxState) {
async function _startScreenSharing(dispatch: Function, state: IReduxState) {
setPictureInPictureEnabled(false);
JitsiMeetJS.createLocalTracks({ devices: [ 'desktop' ] })
.then((tracks: any[]) => {
try {
const tracks: any[] = await JitsiMeetJS.createLocalTracks({ devices: [ 'desktop' ] });
const track = tracks[0];
const currentLocalTrack = getLocalVideoTrack(state['features/base/tracks']);
const currentJitsiTrack = currentLocalTrack?.jitsiTrack;
const currentLocalDesktopTrack = getLocalDesktopTrack(getTrackState(state));
const currentJitsiTrack = currentLocalDesktopTrack?.jitsiTrack;
dispatch(replaceLocalTrack(currentJitsiTrack, track));
// The first time the user shares the screen we add the track and create the transceiver.
// Afterwards, we just replace the old track, so the transceiver will be reused.
if (currentJitsiTrack) {
dispatch(replaceLocalTrack(currentJitsiTrack, track));
} else {
dispatch(addLocalTrack(track));
}
dispatch(setVideoMuted(true, MEDIA_TYPE.VIDEO, VIDEO_MUTISM_AUTHORITY.SCREEN_SHARE));
const { enabled: audioOnly } = state['features/base/audio-only'];
if (audioOnly) {
dispatch(setAudioOnly(false));
}
})
.catch((error: any) => {
} catch (error: any) {
console.log('ERROR creating ScreeSharing stream ', error);
setPictureInPictureEnabled(true);
});
}
}

View File

@@ -313,9 +313,9 @@ export function isLocalTrackMuted(tracks: ITrack[], mediaType: MediaType) {
* @returns {boolean}
*/
export function isLocalVideoTrackDesktop(state: IReduxState) {
const videoTrack = getLocalVideoTrack(getTrackState(state));
const desktopTrack = getLocalDesktopTrack(getTrackState(state));
return videoTrack && videoTrack.videoType === VIDEO_TYPE.DESKTOP;
return desktopTrack !== undefined && !desktopTrack.muted;
}

View File

@@ -1,5 +1,3 @@
// @flow
import { getLogger } from '../base/logging/functions';
export default getLogger('features/chrome-banner');

View File

@@ -1,5 +1,6 @@
import { openDialog } from '../base/dialog';
import { openDialog } from '../base/dialog/actions';
// @ts-ignore
import { DesktopPicker } from './components';
/**
@@ -10,7 +11,7 @@ import { DesktopPicker } from './components';
* a DesktopCapturerSource has been chosen.
* @returns {Object}
*/
export function showDesktopPicker(options = {}, onSourceChoose) {
export function showDesktopPicker(options: { desktopSharingSources?: any; } = {}, onSourceChoose: Function) {
const { desktopSharingSources } = options;
return openDialog(DesktopPicker, {

View File

@@ -1,4 +1,3 @@
import logger from './logger';
/**
@@ -11,8 +10,8 @@ import logger from './logger';
* return native image object used for the preview image of the source.
* @returns {Function}
*/
export function obtainDesktopSources(types, options = {}) {
const capturerOptions = {
export function obtainDesktopSources(types: string[], options: { thumbnailSize?: Object; } = {}) {
const capturerOptions: any = {
types
};
@@ -23,10 +22,10 @@ export function obtainDesktopSources(types, options = {}) {
return new Promise((resolve, reject) => {
const { JitsiMeetElectron } = window;
if (JitsiMeetElectron && JitsiMeetElectron.obtainDesktopStreams) {
if (JitsiMeetElectron?.obtainDesktopStreams) {
JitsiMeetElectron.obtainDesktopStreams(
sources => resolve(_separateSourcesByType(sources)),
error => {
(sources: Array<{ id: string; }>) => resolve(_separateSourcesByType(sources)),
(error: Error) => {
logger.error(
`Error while obtaining desktop sources: ${error}`);
reject(error);
@@ -54,8 +53,8 @@ export function obtainDesktopSources(types, options = {}) {
* @returns {Object} An object with the sources split into separate arrays based
* on source type.
*/
function _separateSourcesByType(sources = []) {
const sourcesByType = {
function _separateSourcesByType(sources: Array<{ id: string; }> = []) {
const sourcesByType: any = {
screen: [],
window: []
};

View File

@@ -1,5 +1,3 @@
// @flow
import { getLogger } from '../base/logging/functions';
export default getLogger('features/desktop-picker');

View File

@@ -32,10 +32,13 @@ MiddlewareRegistry.register(store => next => action => {
inviteDomain
};
// TODO: implement support for gradients.
action.value.avatarBackgrounds = avatarBackgrounds.filter(
(color: string) => !color.includes('linear-gradient')
);
// The backend may send an empty string, make sure we skip that.
if (Array.isArray(avatarBackgrounds)) {
// TODO: implement support for gradients.
action.value.avatarBackgrounds = avatarBackgrounds.filter(
(color: string) => !color.includes('linear-gradient')
);
}
break;
}

View File

@@ -1,12 +1,10 @@
// @flow
/**
* Utility class with no dependencies. Used in components that are stripped in separate bundles
* and requires as less dependencies as possible.
*/
import { getURLWithoutParams } from '../base/connection/utils';
import { doGetJSON } from '../base/util';
import { doGetJSON } from '../base/util/httpUtils';
/**
* Formats the conference pin in readable way for UI to display it.
@@ -49,7 +47,7 @@ export function getDialInConferenceID(
roomName: string,
mucURL: string,
url: URL
): Promise<Object> {
): Promise<any> {
const separator = baseUrl.includes('?') ? '&' : '?';
const conferenceIDURL
= `${baseUrl}${separator}conference=${roomName}@${mucURL}&url=${getURLWithoutParams(url).href}`;
@@ -75,7 +73,7 @@ export function getDialInNumbers(
url: string,
roomName: string,
mucURL: string
): Promise<*> {
): Promise<any> {
const separator = url.includes('?') ? '&' : '?';
// when roomName and mucURL are available

View File

@@ -1,10 +1,7 @@
// @flow
import type { Dispatch } from 'redux';
import { getInviteURL } from '../base/connection';
import { getLocalParticipant, getParticipantCount } from '../base/participants';
import { inviteVideoRooms } from '../videosipgw';
import { IStore } from '../app/types';
import { getInviteURL } from '../base/connection/functions';
import { getLocalParticipant, getParticipantCount } from '../base/participants/functions';
import { inviteVideoRooms } from '../videosipgw/actions';
import { getDialInConferenceID, getDialInNumbers } from './_utils';
import {
@@ -22,6 +19,7 @@ import {
inviteSipEndpoints
} from './functions';
import logger from './logger';
import { IInvitee } from './types';
/**
* Creates a (redux) action to signal that a click/tap has been performed on
@@ -64,11 +62,11 @@ export function hideAddPeopleDialog() {
* of invitees who were not invited (i.e. Invites were not sent to them).
*/
export function invite(
invitees: Array<Object>,
showCalleeInfo: boolean = false) {
invitees: IInvitee[],
showCalleeInfo = false) {
return (
dispatch: Dispatch<any>,
getState: Function): Promise<Array<Object>> => {
dispatch: IStore['dispatch'],
getState: IStore['getState']): Promise<Array<Object>> => {
const state = getState();
const participantsCount = getParticipantCount(state);
const { calleeInfoVisible } = state['features/invite'];
@@ -89,12 +87,12 @@ export function invite(
return new Promise(resolve => {
dispatch(addPendingInviteRequest({
invitees,
callback: failedInvitees => resolve(failedInvitees)
callback: (failedInvitees: any) => resolve(failedInvitees)
}));
});
}
let allInvitePromises = [];
let allInvitePromises: Promise<any>[] = [];
let invitesLeftToSend = [ ...invitees ];
const {
@@ -105,8 +103,8 @@ export function invite(
const inviteUrl = getInviteURL(state);
const { sipInviteUrl } = state['features/base/config'];
const { locationURL } = state['features/base/connection'];
const { jwt } = state['features/base/jwt'];
const { name: displayName } = getLocalParticipant(state);
const { jwt = '' } = state['features/base/jwt'];
const { name: displayName } = getLocalParticipant(state) ?? {};
// First create all promises for dialing out.
const phoneNumbers
@@ -123,7 +121,7 @@ export function invite(
= invitesLeftToSend.filter(
invitee => invitee !== item);
})
.catch(error =>
.catch((error: Error) =>
logger.error('Error inviting phone number:', error));
});
@@ -138,8 +136,8 @@ export function invite(
// filter all rooms and users from {@link invitesLeftToSend}.
const peopleInvitePromise
= invitePeopleAndChatRooms(
callFlowsEnabled
? inviteServiceCallFlowsUrl : inviteServiceUrl,
(callFlowsEnabled
? inviteServiceCallFlowsUrl : inviteServiceUrl) ?? '',
inviteUrl,
jwt,
usersAndRooms)
@@ -173,6 +171,8 @@ export function invite(
conference && inviteSipEndpoints(
sipEndpoints,
// @ts-ignore
locationURL,
sipInviteUrl,
jwt,
@@ -196,12 +196,12 @@ export function invite(
* @returns {Function}
*/
export function updateDialInNumbers() {
return (dispatch: Dispatch<any>, getState: Function) => {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const state = getState();
const { dialInConfCodeUrl, dialInNumbersUrl, hosts }
= state['features/base/config'];
const { numbersFetched } = state['features/invite'];
const mucURL = hosts && hosts.muc;
const mucURL = hosts?.muc;
if (numbersFetched || !dialInConfCodeUrl || !dialInNumbersUrl || !mucURL) {
// URLs for fetching dial in numbers not defined
@@ -209,10 +209,10 @@ export function updateDialInNumbers() {
}
const { locationURL = {} } = state['features/base/connection'];
const { room } = state['features/base/conference'];
const { room = '' } = state['features/base/conference'];
Promise.all([
getDialInNumbers(dialInNumbersUrl, room, mucURL),
getDialInNumbers(dialInNumbersUrl, room, mucURL), // @ts-ignore
getDialInConferenceID(dialInConfCodeUrl, room, mucURL, locationURL)
])
.then(([ dialInNumbers, { conference, id, message, sipUri } ]) => {
@@ -251,7 +251,7 @@ export function updateDialInNumbers() {
*/
export function setCalleeInfoVisible(
calleeInfoVisible: boolean,
initialCalleeInfo: ?Object) {
initialCalleeInfo?: Object) {
return {
type: SET_CALLEE_INFO_VISIBLE,
calleeInfoVisible,
@@ -269,7 +269,7 @@ export function setCalleeInfoVisible(
* }}
*/
export function addPendingInviteRequest(
request: { invitees: Array<Object>, callback: Function }) {
request: { callback: Function; invitees: Array<Object>; }) {
return {
type: ADD_PENDING_INVITE_REQUEST,
request

View File

@@ -1,11 +1,13 @@
// @flow
import type { Dispatch } from 'redux';
import { ADD_PEOPLE_ENABLED, getFeatureFlag } from '../base/flags';
/* eslint-disable lines-around-comment */
import { IStore } from '../app/types';
import { ADD_PEOPLE_ENABLED } from '../base/flags/constants';
import { getFeatureFlag } from '../base/flags/functions';
// @ts-ignore
import { navigate } from '../mobile/navigation/components/conference/ConferenceNavigationContainerRef';
// @ts-ignore
import { screen } from '../mobile/navigation/routes';
import { beginShareRoom } from '../share-room';
import { beginShareRoom } from '../share-room/actions';
/* eslint-enable lines-around-comment */
import { isAddPeopleEnabled, isDialOutEnabled } from './functions';
@@ -18,7 +20,7 @@ export * from './actions.any';
* @returns {Function}
*/
export function doInvitePeople() {
return (dispatch: Dispatch<any>, getState: Function) => {
return (dispatch: IStore['dispatch'], getState: IStore['getState']) => {
const state = getState();
const addPeopleEnabled = getFeatureFlag(state, ADD_PEOPLE_ENABLED, true)
&& (isAddPeopleEnabled(state) || isDialOutEnabled(state));

View File

@@ -205,7 +205,7 @@ function mapStateToProps(state: IReduxState, ownProps: Partial<IProps>) {
const addPeopleEnabled = isAddPeopleEnabled(state);
const dialOutEnabled = isDialOutEnabled(state);
const hideInviteContacts = iAmRecorder || (!addPeopleEnabled && !dialOutEnabled);
const dialIn = state['features/invite'];
const dialIn = state['features/invite']; // @ts-ignore
const phoneNumber = dialIn?.numbers ? _getDefaultPhoneNumber(dialIn.numbers) : undefined;
return {

View File

@@ -1,20 +1,17 @@
// @flow
import { getActiveSession } from '../../features/recording/functions';
import { getRoomName } from '../base/conference';
import { getInviteURL } from '../base/connection';
import { IReduxState } from '../app/types';
import { IStateful } from '../base/app/types';
import { getRoomName } from '../base/conference/functions';
import { getInviteURL } from '../base/connection/functions';
import { isIosMobileBrowser } from '../base/environment/utils';
import { i18next } from '../base/i18n';
import i18next from '../base/i18n/i18next';
import { isJwtFeatureEnabled } from '../base/jwt/functions';
import { JitsiRecordingConstants } from '../base/lib-jitsi-meet';
import { getLocalParticipant, isLocalParticipantModerator } from '../base/participants';
import { toState } from '../base/redux';
import {
appendURLParam,
parseURIString,
parseURLParams
} from '../base/util';
import { getLocalParticipant, isLocalParticipantModerator } from '../base/participants/functions';
import { toState } from '../base/redux/functions';
import { parseURLParams } from '../base/util/parseURLParams';
import { appendURLParam, parseURIString } from '../base/util/uri';
import { isVpaasMeeting } from '../jaas/functions';
import { getActiveSession } from '../recording/functions';
import { getDialInConferenceID, getDialInNumbers } from './_utils';
import {
@@ -24,8 +21,7 @@ import {
} from './constants';
import logger from './logger';
declare var $: Function;
declare var interfaceConfig: Object;
declare let $: any;
export const sharingFeatures = {
email: 'email',
@@ -44,7 +40,7 @@ export const sharingFeatures = {
export function checkDialNumber(
dialNumber: string,
dialOutAuthUrl: string
): Promise<Object> {
): Promise<{ allow?: boolean; country?: string; phone?: string; }> {
const fullUrl = `${dialOutAuthUrl}?phone=${dialNumber}`;
return new Promise((resolve, reject) => {
@@ -61,7 +57,7 @@ export function checkDialNumber(
* numbers.
* @returns {string} A string with only numbers.
*/
export function getDigitsOnly(text: string = ''): string {
export function getDigitsOnly(text = ''): string {
return text.replace(/\D/g, '');
}
@@ -70,41 +66,41 @@ export function getDigitsOnly(text: string = ''): string {
*/
export type GetInviteResultsOptions = {
/**
* The endpoint to use for checking phone number validity.
*/
dialOutAuthUrl: string,
/**
* Whether or not to search for people.
*/
addPeopleEnabled: boolean,
addPeopleEnabled: boolean;
/**
* The endpoint to use for checking phone number validity.
*/
dialOutAuthUrl: string;
/**
* Whether or not to check phone numbers.
*/
dialOutEnabled: boolean,
dialOutEnabled: boolean;
/**
* The jwt token to pass to the search service.
*/
jwt: string;
/**
* Array with the query types that will be executed -
* "conferenceRooms" | "user" | "room".
*/
peopleSearchQueryTypes: Array<string>,
peopleSearchQueryTypes: Array<string>;
/**
* The url to query for people.
*/
peopleSearchUrl: string,
peopleSearchUrl: string;
/**
* Whether or not to check sip invites.
*/
sipInviteEnabled: boolean,
/**
* The jwt token to pass to the search service.
*/
jwt: string
sipInviteEnabled: boolean;
};
/**
@@ -118,8 +114,7 @@ export type GetInviteResultsOptions = {
export function getInviteResultsForQuery(
query: string,
options: GetInviteResultsOptions
): Promise<*> {
): Promise<any> {
const text = query.trim();
const {
@@ -183,12 +178,12 @@ export function getInviteResultsForQuery(
phone: text
});
} else {
phoneNumberPromise = Promise.resolve({});
phoneNumberPromise = Promise.resolve<{ allow?: boolean; country?: string; phone?: string; }>({});
}
return Promise.all([ peopleSearchPromise, phoneNumberPromise ])
.then(([ peopleResults, phoneResults ]) => {
const results = [
const results: any[] = [
...peopleResults
];
@@ -233,7 +228,7 @@ export function getInviteTextiOS({
state,
phoneNumber,
t
}: Object) {
}: { phoneNumber?: string | null; state: IReduxState; t?: Function; }) {
if (!isIosMobileBrowser()) {
return '';
}
@@ -246,23 +241,23 @@ export function getInviteTextiOS({
const inviteURL = _decodeRoomURI(inviteUrl);
let invite = localParticipantName
? t('info.inviteTextiOSPersonal', { name: localParticipantName })
: t('info.inviteURLFirstPartGeneral');
? t?.('info.inviteTextiOSPersonal', { name: localParticipantName })
: t?.('info.inviteURLFirstPartGeneral');
invite += ' ';
invite += t('info.inviteTextiOSInviteUrl', { inviteUrl });
invite += t?.('info.inviteTextiOSInviteUrl', { inviteUrl });
invite += ' ';
if (shouldDisplayDialIn(dialIn) && isSharingEnabled(sharingFeatures.dialIn)) {
invite += t('info.inviteTextiOSPhone', {
invite += t?.('info.inviteTextiOSPhone', {
number: phoneNumber,
conferenceID: dialIn.conferenceID,
didUrl: getDialInfoPageURL(state)
});
}
invite += ' ';
invite += t('info.inviteTextiOSJoinSilent', { silentUrl: `${inviteURL}#config.startSilent=true` });
invite += t?.('info.inviteTextiOSJoinSilent', { silentUrl: `${inviteURL}#config.startSilent=true` });
return invite;
}
@@ -276,27 +271,25 @@ export function getInviteText({
state,
phoneNumber,
t
}: Object) {
}: { phoneNumber?: string | null; state: IReduxState; t?: Function; }) {
const dialIn = state['features/invite'];
const inviteUrl = getInviteURL(state);
const currentLiveStreamingSession = getActiveSession(state, JitsiRecordingConstants.mode.STREAM);
const liveStreamViewURL
= currentLiveStreamingSession
&& currentLiveStreamingSession.liveStreamViewURL;
const liveStreamViewURL = currentLiveStreamingSession?.liveStreamViewURL;
const localParticipant = getLocalParticipant(state);
const localParticipantName = localParticipant?.name;
const inviteURL = _decodeRoomURI(inviteUrl);
let invite = localParticipantName
? t('info.inviteURLFirstPartPersonal', { name: localParticipantName })
: t('info.inviteURLFirstPartGeneral');
? t?.('info.inviteURLFirstPartPersonal', { name: localParticipantName })
: t?.('info.inviteURLFirstPartGeneral');
invite += t('info.inviteURLSecondPart', {
invite += t?.('info.inviteURLSecondPart', {
url: inviteURL
});
if (liveStreamViewURL) {
const liveStream = t('info.inviteLiveStream', {
const liveStream = t?.('info.inviteLiveStream', {
url: liveStreamViewURL
});
@@ -304,11 +297,11 @@ export function getInviteText({
}
if (shouldDisplayDialIn(dialIn) && isSharingEnabled(sharingFeatures.dialIn)) {
const dial = t('info.invitePhone', {
const dial = t?.('info.invitePhone', {
number: phoneNumber,
conferenceID: dialIn.conferenceID
});
const moreNumbers = t('info.invitePhoneAlternatives', {
const moreNumbers = t?.('info.invitePhoneAlternatives', {
url: getDialInfoPageURL(state),
silentUrl: `${inviteURL}#config.startSilent=true`
});
@@ -328,8 +321,8 @@ export function getInviteText({
* @returns {Object} An object with keys as user types and values as the number
* of invites for that type.
*/
export function getInviteTypeCounts(inviteItems: Array<Object> = []) {
const inviteTypeCounts = {};
export function getInviteTypeCounts(inviteItems: Array<{ type: string; }> = []) {
const inviteTypeCounts: any = {};
inviteItems.forEach(({ type }) => {
if (!inviteTypeCounts[type]) {
@@ -352,51 +345,51 @@ export function getInviteTypeCounts(inviteItems: Array<Object> = []) {
* items to invite.
* @returns {Promise} - The promise created by the request.
*/
export function invitePeopleAndChatRooms( // eslint-disable-line max-params
export function invitePeopleAndChatRooms(
inviteServiceUrl: string,
inviteUrl: string,
jwt: string,
inviteItems: Array<Object>
): Promise<void> {
): Promise<any> {
if (!inviteItems || inviteItems.length === 0) {
return Promise.resolve();
}
return fetch(
`${inviteServiceUrl}?token=${jwt}`,
{
body: JSON.stringify({
'invited': inviteItems,
'url': inviteUrl
}),
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
}
`${inviteServiceUrl}?token=${jwt}`,
{
body: JSON.stringify({
'invited': inviteItems,
'url': inviteUrl
}),
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
}
);
}
/**
* Determines if adding people is currently enabled.
*
* @param {boolean} state - Current state.
* @param {IReduxState} state - Current state.
* @returns {boolean} Indication of whether adding people is currently enabled.
*/
export function isAddPeopleEnabled(state: Object): boolean {
export function isAddPeopleEnabled(state: IReduxState): boolean {
const { peopleSearchUrl } = state['features/base/config'];
return state['features/base/jwt'].jwt && Boolean(peopleSearchUrl) && !isVpaasMeeting(state);
return Boolean(state['features/base/jwt'].jwt && Boolean(peopleSearchUrl) && !isVpaasMeeting(state));
}
/**
* Determines if dial out is currently enabled or not.
*
* @param {boolean} state - Current state.
* @param {IReduxState} state - Current state.
* @returns {boolean} Indication of whether dial out is currently enabled.
*/
export function isDialOutEnabled(state: Object): boolean {
export function isDialOutEnabled(state: IReduxState): boolean {
const { conference } = state['features/base/conference'];
return isLocalParticipantModerator(state)
@@ -406,10 +399,10 @@ export function isDialOutEnabled(state: Object): boolean {
/**
* Determines if inviting sip endpoints is enabled or not.
*
* @param {Object} state - Current state.
* @param {IReduxState} state - Current state.
* @returns {boolean} Indication of whether dial out is currently enabled.
*/
export function isSipInviteEnabled(state: Object): boolean {
export function isSipInviteEnabled(state: IReduxState): boolean {
const { sipInviteUrl } = state['features/base/config'];
return isJwtFeatureEnabled(state, 'sip-outbound-call') && Boolean(sipInviteUrl);
@@ -473,7 +466,7 @@ export function searchDirectory( // eslint-disable-line max-params
jwt: string,
text: string,
queryTypes: Array<string> = [ 'conferenceRooms', 'user', 'room' ]
): Promise<Array<Object>> {
): Promise<Array<{ type: string; }>> {
const query = encodeURIComponent(text);
const queryTypesString = encodeURIComponent(JSON.stringify(queryTypes));
@@ -502,14 +495,13 @@ export function searchDirectory( // eslint-disable-line max-params
* Returns descriptive text that can be used to invite participants to a meeting
* (share via mobile or use it for calendar event description).
*
* @param {Object} state - The current state.
* @param {IReduxState} state - The current state.
* @param {string} inviteUrl - The conference/location URL.
* @param {boolean} useHtml - Whether to return html text.
* @returns {Promise<string>} A {@code Promise} resolving with a
* descriptive text that can be used to invite participants to a meeting.
*/
export function getShareInfoText(
state: Object, inviteUrl: string, useHtml: ?boolean): Promise<string> {
export function getShareInfoText(state: IReduxState, inviteUrl: string, useHtml?: boolean): Promise<string> {
let roomUrl = _decodeRoomURI(inviteUrl);
const includeDialInfo = state['features/base/config'] !== undefined;
@@ -534,7 +526,7 @@ export function getShareInfoText(
const { dialInConfCodeUrl, dialInNumbersUrl, hosts }
= state['features/base/config'];
const { locationURL = {} } = state['features/base/connection'];
const mucURL = hosts && hosts.muc;
const mucURL = hosts?.muc;
if (!dialInConfCodeUrl || !dialInNumbersUrl || !mucURL) {
// URLs for fetching dial in numbers not defined
@@ -542,7 +534,7 @@ export function getShareInfoText(
}
numbersPromise = Promise.all([
getDialInNumbers(dialInNumbersUrl, room, mucURL),
getDialInNumbers(dialInNumbersUrl, room, mucURL), // @ts-ignore
getDialInConferenceID(dialInConfCodeUrl, room, mucURL, locationURL)
]).then(([ numbers, {
conference, id, message } ]) => {
@@ -592,16 +584,16 @@ export function getShareInfoText(
/**
* Generates the URL for the static dial in info page.
*
* @param {Object} state - The state from the Redux store.
* @param {IReduxState} state - The state from the Redux store.
* @param {string?} roomName - The conference name. Optional name, if missing will be extracted from state.
* @returns {string}
*/
export function getDialInfoPageURL(state: Object, roomName: ?string) {
export function getDialInfoPageURL(state: IReduxState, roomName?: string) {
const { didPageUrl } = state['features/dynamic-branding'];
const conferenceName = roomName ?? getRoomName(state);
const { locationURL } = state['features/base/connection'];
const { href } = locationURL;
const room = _decodeRoomURI(conferenceName);
const { href = '' } = locationURL ?? {};
const room = _decodeRoomURI(conferenceName ?? '');
const url = didPageUrl || `${href.substring(0, href.lastIndexOf('/'))}/${DIAL_IN_INFO_PAGE_PATH_NAME}`;
@@ -615,7 +607,7 @@ export function getDialInfoPageURL(state: Object, roomName: ?string) {
* @returns {string}
*/
export function getDialInfoPageURLForURIString(
uri: ?string) {
uri?: string) {
if (!uri) {
return undefined;
}
@@ -637,7 +629,7 @@ export function getDialInfoPageURLForURIString(
* @param {Object} dialIn - Dial in information.
* @returns {boolean}
*/
export function shouldDisplayDialIn(dialIn: Object) {
export function shouldDisplayDialIn(dialIn: any) {
const { conferenceID, numbers, numbersEnabled } = dialIn;
const phoneNumber = _getDefaultPhoneNumber(numbers);
@@ -656,7 +648,7 @@ export function shouldDisplayDialIn(dialIn: Object) {
* @private
* @returns {boolean}
*/
export function hasMultipleNumbers(dialInNumbers: ?Object) {
export function hasMultipleNumbers(dialInNumbers?: { numbers: Object; } | string[]) {
if (!dialInNumbers) {
return false;
}
@@ -682,7 +674,7 @@ export function hasMultipleNumbers(dialInNumbers: ?Object) {
* @returns {string|null}
*/
export function _getDefaultPhoneNumber(
dialInNumbers: ?Object): ?string {
dialInNumbers?: { numbers: any; } | Array<{ default: string; formattedNumber: string; }>): string | null {
if (!dialInNumbers) {
return null;
@@ -743,22 +735,23 @@ export function _decodeRoomURI(url: string) {
/**
* Returns the stored conference id.
*
* @param {Object | Function} stateful - The Object or Function that can be
* @param {IStateful} stateful - The Object or Function that can be
* resolved to a Redux state object with the toState function.
* @returns {string}
*/
export function getConferenceId(stateful: Object | Function) {
export function getConferenceId(stateful: IStateful) {
return toState(stateful)['features/invite'].conferenceID;
}
/**
* Returns the default dial in number from the store.
*
* @param {Object | Function} stateful - The Object or Function that can be
* @param {IStateful} stateful - The Object or Function that can be
* resolved to a Redux state object with the toState function.
* @returns {string | null}
*/
export function getDefaultDialInNumber(stateful: Object | Function) {
export function getDefaultDialInNumber(stateful: IStateful) {
// @ts-ignore
return _getDefaultPhoneNumber(toState(stateful)['features/invite'].numbers);
}
@@ -831,14 +824,14 @@ export function isSharingEnabled(sharingFeature: string) {
* @returns {Promise} - The promise created by the request.
*/
export function inviteSipEndpoints( // eslint-disable-line max-params
inviteItems: Array<Object>,
inviteItems: Array<{ address: string; }>,
locationURL: URL,
sipInviteUrl: string,
jwt: string,
roomName: string,
roomPassword: String,
displayName: string
): Promise<void> {
): Promise<any> {
if (inviteItems.length === 0) {
return Promise.resolve();
}
@@ -851,26 +844,26 @@ export function inviteSipEndpoints( // eslint-disable-line max-params
});
return fetch(
sipInviteUrl,
{
body: JSON.stringify({
callParams: {
callUrlInfo: {
baseUrl,
callName: roomName
},
passcode: roomPassword
},
sipClientParams: {
displayName,
sipAddress: inviteItems.map(item => item.address)
}
}),
method: 'POST',
headers: {
'Authorization': `Bearer ${jwt}`,
'Content-Type': 'application/json'
}
}
sipInviteUrl,
{
body: JSON.stringify({
callParams: {
callUrlInfo: {
baseUrl,
callName: roomName
},
passcode: roomPassword
},
sipClientParams: {
displayName,
sipAddress: inviteItems.map(item => item.address)
}
}),
method: 'POST',
headers: {
'Authorization': `Bearer ${jwt}`,
'Content-Type': 'application/json'
}
}
);
}

View File

@@ -1,27 +1,28 @@
// @flow
import { AnyAction } from 'redux';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app';
import {
CONFERENCE_JOINED
} from '../base/conference';
import { IStore } from '../app/types';
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app/actionTypes';
import { CONFERENCE_JOINED } from '../base/conference/actionTypes';
import {
PARTICIPANT_JOINED,
PARTICIPANT_JOINED_SOUND_ID,
PARTICIPANT_LEFT,
PARTICIPANT_UPDATED,
PARTICIPANT_UPDATED
} from '../base/participants/actionTypes';
import { pinParticipant } from '../base/participants/actions';
import { PARTICIPANT_JOINED_SOUND_ID } from '../base/participants/constants';
import {
getLocalParticipant,
getParticipantCount,
getParticipantPresenceStatus,
getRemoteParticipants,
pinParticipant
} from '../base/participants';
import { MiddlewareRegistry } from '../base/redux';
getRemoteParticipants
} from '../base/participants/functions';
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
import {
playSound,
registerSound,
stopSound,
unregisterSound
} from '../base/sounds';
} from '../base/sounds/actions';
import {
CALLING,
CONNECTED_USER,
@@ -29,7 +30,7 @@ import {
INVITED,
REJECTED,
RINGING
} from '../presence-status';
} from '../presence-status/constants';
import {
SET_CALLEE_INFO_VISIBLE,
@@ -49,8 +50,6 @@ import {
import logger from './logger';
import { sounds } from './sounds';
declare var interfaceConfig: Object;
/**
* Maps the presence status with the ID of the sound that will be played when
* the status is received.
@@ -84,7 +83,7 @@ MiddlewareRegistry.register(store => next => action => {
if (action.type === SET_CALLEE_INFO_VISIBLE) {
if (action.calleeInfoVisible) {
dispatch(pinParticipant(getLocalParticipant(state).id));
dispatch(pinParticipant(getLocalParticipant(state)?.id));
} else {
// unpin participant
dispatch(pinParticipant());
@@ -124,10 +123,10 @@ MiddlewareRegistry.register(store => next => action => {
const oldSoundId
= oldParticipantPresence
&& statusToRingtone[oldParticipantPresence];
&& statusToRingtone[oldParticipantPresence as keyof typeof statusToRingtone];
const newSoundId
= newParticipantPresence
&& statusToRingtone[newParticipantPresence];
&& statusToRingtone[newParticipantPresence as keyof typeof statusToRingtone];
if (oldSoundId === newSoundId) {
@@ -159,10 +158,10 @@ MiddlewareRegistry.register(store => next => action => {
* (not poltergeist, shared video, etc.) participants in the call.
*
* @param {Object} action - The redux action.
* @param {ReduxStore} store - The redux store.
* @param {IStore} store - The redux store.
* @returns {void}
*/
function _maybeHideCalleeInfo(action, store) {
function _maybeHideCalleeInfo(action: AnyAction, store: IStore) {
const state = store.getState();
if (!state['features/invite'].calleeInfoVisible) {
@@ -188,10 +187,10 @@ function _maybeHideCalleeInfo(action, store) {
/**
* Executes the pending invitation requests if any.
*
* @param {ReduxStore} store - The redux store.
* @param {IStore} store - The redux store.
* @returns {void}
*/
function _onConferenceJoined(store) {
function _onConferenceJoined(store: IStore) {
const { dispatch, getState } = store;
const pendingInviteRequests

View File

@@ -1,10 +1,11 @@
// @flow
import { AnyAction } from 'redux';
import { hideDialog, openDialog } from '../base/dialog';
import { MiddlewareRegistry } from '../base/redux';
import { IStore } from '../app/types';
import { hideDialog, openDialog } from '../base/dialog/actions';
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
import { BEGIN_ADD_PEOPLE, HIDE_ADD_PEOPLE_DIALOG } from './actionTypes';
import { AddPeopleDialog } from './components';
import AddPeopleDialog from './components/add-people-dialog/web/AddPeopleDialog';
import './middleware.any';
/**
@@ -37,7 +38,7 @@ MiddlewareRegistry.register(store => next => action => {
* @private
* @returns {*} The value returned by {@code next(action)}.
*/
function _beginAddPeople({ dispatch }, next, action) {
function _beginAddPeople({ dispatch }: IStore, next: Function, action: AnyAction) {
const result = next(action);
dispatch(openDialog(AddPeopleDialog));
@@ -58,7 +59,7 @@ function _beginAddPeople({ dispatch }, next, action) {
* @private
* @returns {*} The value returned by {@code next(action)}.
*/
function _hideAddPeopleDialog({ dispatch }, next, action) {
function _hideAddPeopleDialog({ dispatch }: IStore, next: Function, action: AnyAction) {
dispatch(hideDialog(AddPeopleDialog));
return next(action);

View File

@@ -8,6 +8,7 @@ import {
UPDATE_DIAL_IN_NUMBERS_SUCCESS
} from './actionTypes';
import logger from './logger';
import { IInvitee } from './types';
const DEFAULT_STATE = {
/**
@@ -27,12 +28,12 @@ export interface IInviteState {
conferenceID?: string | number;
error?: Error;
initialCalleeInfo?: Object;
numbers?: string;
numbers?: string[];
numbersEnabled: boolean;
numbersFetched: boolean;
pendingInviteRequests: Array<{
callback: Function;
invitees: Array<Object>;
invitees: IInvitee[];
}>;
sipUri?: string;
}

View File

@@ -0,0 +1,5 @@
export interface IInvitee {
address: string;
number: string;
type: string;
}

View File

@@ -92,7 +92,7 @@ function pollForStatus(
return;
}
const res = await executeDialOutStatusRequest(getDialOutStatusUrl(state), reqId);
const res = await executeDialOutStatusRequest(getDialOutStatusUrl(state) ?? '', reqId);
switch (res) {
case DIAL_OUT_STATUS.INITIATED:
@@ -153,7 +153,7 @@ export function dialOut(onSuccess: Function, onFail: Function) {
return async function(dispatch: IStore['dispatch'], getState: IStore['getState']) {
const state = getState();
const reqId = uuidv4();
const url = getDialOutUrl(state);
const url = getDialOutUrl(state) ?? '';
const conferenceUrl = getDialOutConferenceUrl(state);
const phoneNumber = getFullDialOutNumber(state);
const countryCode = getDialOutCountry(state).code.toUpperCase();

View File

@@ -1,5 +1,3 @@
// @flow
import {
DELETE_RECENT_LIST_ENTRY,
_STORE_CURRENT_CONFERENCE,

View File

@@ -1,9 +1,13 @@
import {
getLocalizedDateFormatter,
getLocalizedDurationFormatter
} from '../base/i18n';
} from '../base/i18n/dateUtil';
// eslint-disable-next-line lines-around-comment
// @ts-ignore
import { NavigateSectionList } from '../base/react';
import { parseURIString, safeDecodeURIComponent } from '../base/util';
import { parseURIString, safeDecodeURIComponent } from '../base/util/uri';
import { IRecentItem } from './types';
/**
* Creates a displayable list item of a recent list entry.
@@ -14,7 +18,8 @@ import { parseURIString, safeDecodeURIComponent } from '../base/util';
* @param {Function} t - The translate function.
* @returns {Object}
*/
function toDisplayableItem(item, defaultServerURL, t) {
function toDisplayableItem(item: IRecentItem,
defaultServerURL: string, t: Function) {
const location = parseURIString(item.conference);
const baseURL = `${location.protocol}//${location.host}`;
const serverName = baseURL === defaultServerURL ? null : location.host;
@@ -43,7 +48,7 @@ function toDisplayableItem(item, defaultServerURL, t) {
* @param {number} duration - The item's duration.
* @returns {string}
*/
function _toDurationString(duration) {
function _toDurationString(duration: number) {
if (duration) {
return getLocalizedDurationFormatter(duration);
}
@@ -59,7 +64,7 @@ function _toDurationString(duration) {
* @param {Function} t - The translate function.
* @returns {string}
*/
function _toDateString(itemDate, t) {
function _toDateString(itemDate: number, t: Function) {
const m = getLocalizedDateFormatter(itemDate);
const date = new Date(itemDate);
const dateInMs = date.getTime();
@@ -90,7 +95,8 @@ function _toDateString(itemDate, t) {
* @param {string} defaultServerURL - The default server URL.
* @returns {Array<Object>}
*/
export function toDisplayableList(recentList, t, defaultServerURL) {
export function toDisplayableList(recentList: IRecentItem[],
t: Function, defaultServerURL: string) {
const { createSection } = NavigateSectionList;
const todaySection = createSection(t('dateUtils.today'), 'today');
const yesterdaySection

View File

@@ -1,6 +1,4 @@
/* global interfaceConfig */
import { parseURIString, safeDecodeURIComponent } from '../base/util';
import { parseURIString, safeDecodeURIComponent } from '../base/util/uri';
/**
@@ -10,7 +8,7 @@ import { parseURIString, safeDecodeURIComponent } from '../base/util';
* @param {Array<Object>} recentList - The recent list form the redux store.
* @returns {Array<Object>}
*/
export function toDisplayableList(recentList) {
export function toDisplayableList(recentList: Array<{ conference: string; date: Date; duration: number; }>) {
return (
[ ...recentList ].reverse()
.map(item => {

View File

@@ -1,5 +1,3 @@
// @flow
import { getLogger } from '../base/logging/functions';
export default getLogger('features/recent-list');

View File

@@ -1,21 +1,17 @@
// @flow
import { AnyAction } from 'redux';
import { APP_WILL_MOUNT } from '../base/app';
import {
CONFERENCE_WILL_LEAVE,
JITSI_CONFERENCE_URL_KEY,
SET_ROOM
} from '../base/conference';
import { addKnownDomains } from '../base/known-domains';
import { MiddlewareRegistry } from '../base/redux';
import { parseURIString } from '../base/util';
import { IStore } from '../app/types';
import { APP_WILL_MOUNT } from '../base/app/actionTypes';
import { CONFERENCE_WILL_LEAVE, SET_ROOM } from '../base/conference/actionTypes';
import { JITSI_CONFERENCE_URL_KEY } from '../base/conference/constants';
import { addKnownDomains } from '../base/known-domains/actions';
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
import { inIframe } from '../base/util/iframeUtils';
import { parseURIString } from '../base/util/uri';
import { _storeCurrentConference, _updateConferenceDuration } from './actions';
import { isRecentListEnabled } from './functions';
declare var APP: Object;
/**
* Middleware that captures joined rooms so they can be saved into
* {@code window.localStorage}.
@@ -53,7 +49,7 @@ MiddlewareRegistry.register(store => next => action => {
* @private
* @returns {*} The result returned by {@code next(action)}.
*/
function _appWillMount({ dispatch, getState }, next, action) {
function _appWillMount({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
const result = next(action);
// It's an opportune time to transfer the feature recent-list's knowledge
@@ -86,7 +82,7 @@ function _appWillMount({ dispatch, getState }, next, action) {
* @private
* @returns {*} The result returned by {@code next(action)}.
*/
function _conferenceWillLeave({ dispatch, getState }, next, action) {
function _conferenceWillLeave({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
const { doNotStoreRoom } = getState()['features/base/config'];
if (!doNotStoreRoom && !inIframe()) {
@@ -126,7 +122,7 @@ function _conferenceWillLeave({ dispatch, getState }, next, action) {
* @private
* @returns {*} The result returned by {@code next(action)}.
*/
function _setRoom({ dispatch, getState }, next, action) {
function _setRoom({ dispatch, getState }: IStore, next: Function, action: AnyAction) {
const { doNotStoreRoom } = getState()['features/base/config'];
if (!doNotStoreRoom && !inIframe() && action.room) {

View File

@@ -0,0 +1,5 @@
export interface IRecentItem {
conference: string;
date: number;
duration: number;
}

View File

@@ -1,13 +1,14 @@
// @flow
import { getLocalJitsiDesktopTrack, getLocalJitsiVideoTrack } from '../../features/base/tracks';
import { getMultipleVideoSendingSupportFeatureFlag } from '../base/config';
import { IStore } from '../app/types';
import { getMultipleVideoSendingSupportFeatureFlag } from '../base/config/functions';
import { getLocalJitsiDesktopTrack, getLocalJitsiVideoTrack } from '../base/tracks/functions';
import { SET_SCREENSHOT_CAPTURE } from './actionTypes';
// eslint-disable-next-line lines-around-comment
// @ts-ignore
import { createScreenshotCaptureSummary } from './functions';
import logger from './logger';
let screenshotSummary;
let screenshotSummary: any;
/**
* Marks the on-off state of screenshot captures.
@@ -18,7 +19,7 @@ let screenshotSummary;
* payload: enabled
* }}
*/
function setScreenshotCapture(enabled) {
function setScreenshotCapture(enabled: boolean) {
return {
type: SET_SCREENSHOT_CAPTURE,
payload: enabled
@@ -32,7 +33,7 @@ function setScreenshotCapture(enabled) {
* @returns {Promise}
*/
export function toggleScreenshotCaptureSummary(enabled: boolean) {
return async function(dispatch: (Object) => Object, getState: () => any) {
return async function(dispatch: IStore['dispatch'], getState: IStore['getState']) {
const state = getState();
if (state['features/screenshot-capture'].capturesEnabled !== enabled) {

View File

@@ -1,5 +1,3 @@
// @flow
import { getLogger } from '../base/logging/functions';
export default getLogger('features/screenshot-capture');

View File

@@ -1,10 +1,9 @@
// @flow
import { Share } from 'react-native';
import { getName } from '../app/functions';
import { MiddlewareRegistry } from '../base/redux';
import { getShareInfoText } from '../invite';
import { getName } from '../app/functions.native';
import { IStore } from '../app/types';
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
import { getShareInfoText } from '../invite/functions';
import { BEGIN_SHARE_ROOM } from './actionTypes';
import { endShareRoom } from './actions';
@@ -35,7 +34,7 @@ MiddlewareRegistry.register(store => next => action => {
* @private
* @returns {void}
*/
function _shareRoom(roomURL: string, { dispatch, getState }) {
function _shareRoom(roomURL: string, { dispatch, getState }: IStore) {
getShareInfoText(getState(), roomURL)
.then(message => {
const title = `${getName()} Conference`;

View File

@@ -17,5 +17,17 @@ export function isWelcomePageEnabled(stateful: IStateful) {
return getFeatureFlag(stateful, WELCOME_PAGE_ENABLED, false);
}
return toState(stateful)['features/base/config'].enableWelcomePage;
const config = toState(stateful)['features/base/config'];
return !config.welcomePage?.disabled;
}
/**
* Returns the configured custom URL (if any) to redirect to instead of the normal landing page.
*
* @param {IStateful} stateful - The redux state or {@link getState}.
* @returns {string} - The custom URL.
*/
export function getCustomLandingPageURL(stateful: IStateful) {
return toState(stateful)['features/base/config'].welcomePage?.customUrl;
}

View File

@@ -5,6 +5,7 @@ local log = module._log;
local host = module.host;
local st = require "util.stanza";
local um_is_admin = require "core.usermanager".is_admin;
local jid_split = require "util.jid".split;
local function is_admin(jid)
@@ -39,8 +40,11 @@ log("debug",
-- option to disable room modification (sending muc config form) for guest that do not provide token
local require_token_for_moderation;
-- option to not require tokens for certain users and domains
local token_empty_allow_list;
local function load_config()
require_token_for_moderation = module:get_option_boolean("token_verification_require_token_for_moderation");
token_empty_allow_list = module:get_option_set("token_verification_empty_allow_list");
end
load_config();
@@ -57,6 +61,19 @@ local function verify_user(session, stanza)
return true;
end
-- if token is empty and user matches allow list, skip verification and allow user to join
local user, domain, res = jid_split(user_jid);
if session.auth_token == nil and user ~= nil and domain ~= nil and token_empty_allow_list then
if token_empty_allow_list:contains(domain) then
log("debug", "Token not required from user: %s in allowed domain: %s", user_jid, domain);
return true;
end
if token_empty_allow_list:contains(user..'@'..domain) then
log("debug", "Token not required from user in allowed list: %s", user_jid);
return true;
end
end
log("debug",
"Will verify token for user: %s, room: %s ", user_jid, stanza.attr.to);
if not token_util:verify_room(session, stanza.attr.to) then

View File

@@ -6,6 +6,7 @@
<!--#include virtual="/base.html" -->
<link rel="stylesheet" href="css/all.css">
<script>
window.EXCALIDRAW_ASSET_PATH = 'libs/';
document.addEventListener('DOMContentLoaded', () => {
if (!JitsiMeetJS.app) {
return;

View File

@@ -16,6 +16,7 @@
},
"exclude": [
"node_modules",
"react/features/share-room",
"**/mobile/*",
"**/native/*",
"**/*.native.ts",