Compare commits

...

13 Commits

Author SHA1 Message Date
Calin-Teodor
16597a2535 feat(android): list media projection permission in manifest 2024-04-18 17:14:25 +03:00
Saúl Ibarra Corretgé
2f3cf9f530 chore(deps) lib-jitsi-meet@latest
https://github.com/jitsi/lib-jitsi-meet/compare/v1811.0.0+86e2fb2b...v1812.0.0+2eddb859
2024-04-18 15:11:59 +02:00
Дамян Минков
347cc32ecc chore(deps) lib-jitsi-meet@latest (#14642) 2024-04-16 19:53:23 -04:00
Calin-Teodor
c0602abbca feat(base/config): whitelist customToolbarButtons 2024-04-16 17:39:20 +03:00
Calin-Teodor
55e9136b91 feat(toolbox/native): fixed icon for CustomOptionButton and styles 2024-04-16 16:21:12 +03:00
damencho
da01ca23db fix(visitors): Fixes promote all. 2024-04-16 06:20:06 -05:00
Hristo Terezov
b470c201b2 fix(conference): use up to date state in useVideoStream. 2024-04-12 16:08:21 -05:00
Mihaela Dumitru
e3ee99754c fix(debug) enable webview debugging (#14623) 2024-04-12 16:32:45 +03:00
Mihaela Dumitru
b3e1865fd8 fix(whiteboard/native) disable Android local storage 2024-04-12 12:28:50 +02:00
Saúl Ibarra Corretgé
a4e3716632 chore(rn,deps) react-native-webrtc@118.0.6
Fixes duplicated audio on iOS.
2024-04-11 23:17:20 +02:00
Hristo Terezov
5ed4b470e7 feat(visitors-config): Enable media on promotion. 2024-04-11 15:48:23 -05:00
damencho
68f030bb7f feat: Adds option to print not-allowed errors we sent back to client. 2024-04-11 10:45:39 -05:00
Hazoom
1c25a370be fix (invite-copy) give user a feedback that copy is done (#14552) 2024-04-11 16:28:34 +03:00
17 changed files with 228 additions and 102 deletions

View File

@@ -13,6 +13,7 @@
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
<uses-feature
android:glEsVersion="0x00020000"

View File

@@ -1337,12 +1337,11 @@ export default {
* @returns {Promise}
*/
useVideoStream(newTrack) {
const state = APP.store.getState();
logger.debug(`useVideoStream: ${newTrack}`);
return new Promise((resolve, reject) => {
_replaceLocalVideoTrackQueue.enqueue(onFinish => {
const state = APP.store.getState();
const oldTrack = getLocalJitsiVideoTrack(state);
logger.debug(`useVideoStream: Replacing ${oldTrack} with ${newTrack}`);

View File

@@ -453,7 +453,7 @@ PODS:
- react-native-video/Video (6.0.0-alpha.11):
- PromisesSwift
- React-Core
- react-native-webrtc (118.0.5):
- react-native-webrtc (118.0.6):
- JitsiWebRTC (~> 118.0.0)
- React-Core
- react-native-webview (13.5.1):
@@ -881,7 +881,7 @@ SPEC CHECKSUMS:
react-native-slider: 1cdd6ba29675df21f30544253bf7351d3c2d68c4
react-native-splash-screen: 4312f786b13a81b5169ef346d76d33bc0c6dc457
react-native-video: 472b7c366eaaaa0207e546d9a50410df89790bcf
react-native-webrtc: 88fb4b2994b06b4d6ad67aee7f8ce7e7aa7012bd
react-native-webrtc: 4a9428c3ced472d3b08e1fbc4bfdc082692f2d5d
react-native-webview: 8baa0f5c6d336d6ba488e942bcadea5bf51f050a
React-NativeModulesApple: 4225ac31a26696c02c54b471052b3e85e74a9a0c
React-perflogger: cb433f318c6667060fc1f62e26eb58d6eb30a627

24
package-lock.json generated
View File

@@ -61,7 +61,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/v1807.0.0+b59b8cb0/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1812.0.0+2eddb859/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.4",
"moment-duration-format": "2.2.2",
@@ -100,7 +100,7 @@
"react-native-url-polyfill": "2.0.0",
"react-native-video": "6.0.0-alpha.11",
"react-native-watch-connectivity": "1.1.0",
"react-native-webrtc": "118.0.5",
"react-native-webrtc": "118.0.6",
"react-native-webview": "13.5.1",
"react-native-youtube-iframe": "2.3.0",
"react-redux": "7.2.9",
@@ -12866,8 +12866,8 @@
},
"node_modules/lib-jitsi-meet": {
"version": "0.0.0",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1807.0.0+b59b8cb0/lib-jitsi-meet.tgz",
"integrity": "sha512-M51XeZWqPiSUzRNz8JPxbi45iBBYzK3Z+7l/MFDGBoDtNNe3KXnWUjwL/F6PK/AL1VlN5nvp51sxQ8K/gOXl1g==",
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1812.0.0+2eddb859/lib-jitsi-meet.tgz",
"integrity": "sha512-svKmkJzs6BNrOgTHST7aAxpGHLAVAlxuX7VQrGVL8HUpU6FDrqHBRSMQXX0937jir6OpIhSCjoFK6maZ9xfOZg==",
"hasInstallScript": true,
"license": "Apache-2.0",
"dependencies": {
@@ -16914,9 +16914,9 @@
}
},
"node_modules/react-native-webrtc": {
"version": "118.0.5",
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-118.0.5.tgz",
"integrity": "sha512-R665M4V8pJhpOvFT0iD0T70TNxX1Q92ikt7h8uTLaNDt08OymlnJ+5S95XS2qIDRUjN1aMjjnzS+HlmZ4C1AQg==",
"version": "118.0.6",
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-118.0.6.tgz",
"integrity": "sha512-4PD22zk3MPYCZnEz2n8jVOqopzrawrWRkrPQfZ6sElH2HgPyZXpVXjt5uIMiAHHaS0nxqqKSuKNzNELfTNvvUg==",
"dependencies": {
"base64-js": "1.5.1",
"debug": "4.3.4",
@@ -29374,8 +29374,8 @@
}
},
"lib-jitsi-meet": {
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1807.0.0+b59b8cb0/lib-jitsi-meet.tgz",
"integrity": "sha512-M51XeZWqPiSUzRNz8JPxbi45iBBYzK3Z+7l/MFDGBoDtNNe3KXnWUjwL/F6PK/AL1VlN5nvp51sxQ8K/gOXl1g==",
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1812.0.0+2eddb859/lib-jitsi-meet.tgz",
"integrity": "sha512-svKmkJzs6BNrOgTHST7aAxpGHLAVAlxuX7VQrGVL8HUpU6FDrqHBRSMQXX0937jir6OpIhSCjoFK6maZ9xfOZg==",
"requires": {
"@jitsi/js-utils": "2.2.1",
"@jitsi/logger": "2.0.2",
@@ -32312,9 +32312,9 @@
}
},
"react-native-webrtc": {
"version": "118.0.5",
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-118.0.5.tgz",
"integrity": "sha512-R665M4V8pJhpOvFT0iD0T70TNxX1Q92ikt7h8uTLaNDt08OymlnJ+5S95XS2qIDRUjN1aMjjnzS+HlmZ4C1AQg==",
"version": "118.0.6",
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-118.0.6.tgz",
"integrity": "sha512-4PD22zk3MPYCZnEz2n8jVOqopzrawrWRkrPQfZ6sElH2HgPyZXpVXjt5uIMiAHHaS0nxqqKSuKNzNELfTNvvUg==",
"requires": {
"base64-js": "1.5.1",
"debug": "4.3.4",

View File

@@ -67,7 +67,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/v1807.0.0+b59b8cb0/lib-jitsi-meet.tgz",
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1812.0.0+2eddb859/lib-jitsi-meet.tgz",
"lodash": "4.17.21",
"moment": "2.29.4",
"moment-duration-format": "2.2.2",
@@ -106,7 +106,7 @@
"react-native-url-polyfill": "2.0.0",
"react-native-video": "6.0.0-alpha.11",
"react-native-watch-connectivity": "1.1.0",
"react-native-webrtc": "118.0.5",
"react-native-webrtc": "118.0.6",
"react-native-webview": "13.5.1",
"react-native-youtube-iframe": "2.3.0",
"react-redux": "7.2.9",

View File

@@ -1,3 +1,5 @@
// @ts-expect-error
import UIEvents from '../../../../service/UI/UIEvents';
import { createStartMutedConfigurationEvent } from '../../analytics/AnalyticsEvents';
import { sendAnalytics } from '../../analytics/functions';
import { IReduxState, IStore } from '../../app/types';
@@ -7,6 +9,7 @@ import { overwriteConfig } from '../config/actions';
import { getReplaceParticipant } from '../config/functions';
import { connect, disconnect, hangup } from '../connection/actions';
import { JITSI_CONNECTION_CONFERENCE_KEY } from '../connection/constants';
import { hasAvailableDevices } from '../devices/functions.any';
import { JitsiConferenceEvents, JitsiE2ePingEvents } from '../lib-jitsi-meet';
import {
gumPending,
@@ -15,7 +18,7 @@ import {
setVideoMuted,
setVideoUnmutePermissions
} from '../media/actions';
import { MEDIA_TYPE } from '../media/constants';
import { MEDIA_TYPE, VIDEO_MUTISM_AUTHORITY } from '../media/constants';
import { IGUMPendingState } from '../media/types';
import {
dominantSpeakerChanged,
@@ -1059,13 +1062,45 @@ export function redirect(vnode: string, focusJid: string, username: string) {
.then(() => dispatch(conferenceWillInit()))
.then(() => dispatch(connect()))
.then(() => {
// Clear the gum pending state in case we have set it to pending since we are starting the
// conference without tracks.
dispatch(gumPending([ MEDIA_TYPE.AUDIO, MEDIA_TYPE.VIDEO ], IGUMPendingState.NONE));
// FIXME: Workaround for the web version. To be removed once we get rid of conference.js
if (typeof APP !== 'undefined') {
if (!vnode) {
const state = getState();
const { enableMediaOnPromote = {} } = state['features/base/config'].visitors ?? {};
const { audio = false, video = false } = enableMediaOnPromote;
if (audio) {
const { available, muted, unmuteBlocked } = state['features/base/media'].audio;
const { startSilent } = state['features/base/config'];
// do not unmute the user if he was muted before (on the prejoin, the config
// or URL param, etc.)
if (!unmuteBlocked && !muted && !startSilent && available) {
dispatch(setAudioMuted(false, true));
// // FIXME: The old conference logic still relies on this event being emitted.
typeof APP === 'undefined' || APP.UI.emitEvent(UIEvents.AUDIO_MUTED, false);
}
}
if (video) {
const { muted, unmuteBlocked } = state['features/base/media'].video;
// do not unmute the user if he was muted before (on the prejoin, the config, URL param or
// audo only, etc)
if (!unmuteBlocked && !muted && hasAvailableDevices(state, 'videoInput')) {
dispatch(setVideoMuted(false, VIDEO_MUTISM_AUTHORITY.USER, true));
// // FIXME: The old conference logic still relies on this event being emitted.
typeof APP === 'undefined' || APP.UI.emitEvent(UIEvents.VIDEO_MUTED, false);
}
}
}
APP.conference.startConference([]);
}
});

View File

@@ -76,6 +76,7 @@ export default [
'channelLastN',
'connectionIndicators',
'constraints',
'customToolbarButtons',
'brandingRoomAlias',
'debug',
'debugAudioLevels',
@@ -227,6 +228,7 @@ export default [
'useHostPageLocalStorage',
'useTurnUdp',
'videoQuality',
'visitors',
'watchRTCConfigParams',
'webrtcIceTcpDisable',
'webrtcIceUdpDisable',

View File

@@ -75,6 +75,12 @@ export interface IConfigState extends IConfig {
p2p?: object;
websocket?: string;
};
visitors?: {
enableMediaOnPromote?: {
audio?: boolean;
video?: boolean;
};
};
}
ReducerRegistry.register<IConfigState>('features/base/config', (state = _getInitialState(), action): IConfigState => {

View File

@@ -60,7 +60,8 @@ class SharedDocument extends PureComponent<IProps> {
renderLoading = { this._renderLoading }
source = {{ uri: _documentUrl ?? '' }}
startInLoadingState = { true }
style = { styles.sharedDoc } />
style = { styles.sharedDoc }
webviewDebuggingEnabled = { true } />
</JitsiScreen>
);
}

View File

@@ -1,12 +1,18 @@
import React, { Component } from 'react';
import React, { useEffect, useState } from 'react';
import { WithTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { translate } from '../../../../base/i18n/functions';
import Icon from '../../../../base/icons/components/Icon';
import { IconCopy } from '../../../../base/icons/svg';
import { IconCheck, IconCopy } from '../../../../base/icons/svg';
import Tooltip from '../../../../base/tooltip/components/Tooltip';
import { copyText } from '../../../../base/util/copyText.web';
import { showSuccessNotification } from '../../../../notifications/actions';
import { NOTIFICATION_TIMEOUT_TYPE } from '../../../../notifications/constants';
import { _formatConferenceIDPin } from '../../../_utils';
let mounted: boolean;
/**
* The type of the React {@code Component} props of {@link DialInNumber}.
*/
@@ -25,83 +31,108 @@ interface IProps extends WithTranslation {
phoneNumber: string;
}
/**
* React {@code Component} responsible for displaying a telephone number and
* conference ID for dialing into a conference.
* Component responsible for displaying a telephone number and
* conference ID for dialing into a conference and copying them to clipboard.
*
* @augments Component
* @returns {ReactNode}
*/
class DialInNumber extends Component<IProps> {
function DialInNumber({ conferenceID, phoneNumber, t }: IProps) {
const dispatch = useDispatch();
const [ isClicked, setIsClicked ] = useState(false);
const dialInLabel = t('info.dialInNumber');
const passcode = t('info.dialInConferenceID');
const conferenceIDPin = `${_formatConferenceIDPin(conferenceID)}#`;
const textToCopy = `${dialInLabel} ${phoneNumber} ${passcode} ${conferenceIDPin}`;
useEffect(() => {
mounted = true;
return () => {
mounted = false;
};
}, []);
/**
* Initializes a new DialInNumber instance.
* Copies the conference ID and phone number to the clipboard.
*
* @param {Object} props - The read-only properties with which the new
* instance is to be initialized.
*/
constructor(props: IProps) {
super(props);
* @returns {void}
*/
function _onCopyText() {
copyText(textToCopy);
dispatch(showSuccessNotification({
titleKey: 'dialog.copied'
}, NOTIFICATION_TIMEOUT_TYPE.SHORT));
setIsClicked(true);
setTimeout(() => {
// avoid: Can't perform a React state update on an unmounted component
if (mounted) {
setIsClicked(false);
}
}, 2500);
// Bind event handler so it is only bound once for every instance.
this._onCopyText = this._onCopyText.bind(this);
}
/**
* Copies the dial-in information to the clipboard.
* Copies the conference invitation to the clipboard.
*
* @param {Object} e - The key event to handle.
*
* @returns {void}
*/
_onCopyText() {
const { conferenceID, phoneNumber, t } = this.props;
const dialInLabel = t('info.dialInNumber');
const passcode = t('info.dialInConferenceID');
const conferenceIDPin = `${_formatConferenceIDPin(conferenceID)}#`;
const textToCopy = `${dialInLabel} ${phoneNumber} ${passcode} ${conferenceIDPin}`;
copyText(textToCopy);
function _onCopyTextKeyPress(e: React.KeyboardEvent) {
if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault();
_onCopyText();
}
}
/**
* Implements React's {@link Component#render()}.
* Renders section that shows the phone number and conference ID
* and give user the ability to copy them to the clipboard.
*
* @inheritdoc
* @returns {ReactElement}
* @returns {ReactNode}
*/
render() {
const { conferenceID, phoneNumber, t } = this.props;
return (
<div className = 'dial-in-number'>
<p>
<span className = 'phone-number'>
<span className = 'info-label'>
{ t('info.dialInNumber') }
</span>
<span className = 'spacer'>&nbsp;</span>
<span className = 'info-value'>
{ phoneNumber }
</span>
return (
<div className = 'dial-in-number'>
<p>
<span className = 'phone-number'>
<span className = 'info-label'>
{ t('info.dialInNumber') }
</span>
<br />
<span className = 'conference-id'>
<span className = 'info-label'>
{ t('info.dialInConferenceID') }
</span>
<span className = 'spacer'>&nbsp;</span>
<span className = 'info-value'>
{ `${_formatConferenceIDPin(conferenceID)}#` }
</span>
<span className = 'spacer'>&nbsp;</span>
<span className = 'info-value'>
{ phoneNumber }
</span>
</p>
</span>
<br />
<span className = 'conference-id'>
<span className = 'info-label'>
{ t('info.dialInConferenceID') }
</span>
<span className = 'spacer'>&nbsp;</span>
<span className = 'info-value'>
{ `${_formatConferenceIDPin(conferenceID)}#` }
</span>
</span>
</p>
<Tooltip
content = { t('info.copyNumber') }
position = 'top'>
<button
aria-label = { t('info.copyNumber') }
className = 'dial-in-copy invisible-button'
onClick = { this._onCopyText }>
<Icon src = { IconCopy } />
// eslint-disable-next-line react/jsx-no-bind
onClick = { _onCopyText }
// eslint-disable-next-line react/jsx-no-bind
onKeyPress = { _onCopyTextKeyPress }>
<Icon src = { isClicked ? IconCheck : IconCopy } />
</button>
</div>
);
}
</Tooltip>
</div>
);
}
export default translate(DialInNumber);

View File

@@ -1,11 +1,13 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import React, { useEffect, useState } from 'react';
import { WithTranslation } from 'react-i18next';
import { useDispatch } from 'react-redux';
import { makeStyles } from 'tss-react/mui';
import { isIosMobileBrowser } from '../../../../base/environment/utils';
import { translate } from '../../../../base/i18n/functions';
import Icon from '../../../../base/icons/components/Icon';
import {
IconCheck,
IconCopy,
IconEnvelope,
IconGoogle,
@@ -16,7 +18,10 @@ import Tooltip from '../../../../base/tooltip/components/Tooltip';
import { copyText } from '../../../../base/util/copyText.web';
import { showSuccessNotification } from '../../../../notifications/actions';
import { NOTIFICATION_TIMEOUT_TYPE } from '../../../../notifications/constants';
interface IProps {
let mounted: boolean;
interface IProps extends WithTranslation {
/**
* The encoded invitation subject.
@@ -63,26 +68,41 @@ const useStyles = makeStyles()(theme => {
*
* @returns {ReactNode}
*/
function InviteByEmailSection({ inviteSubject, inviteText, inviteTextiOS }: IProps) {
function InviteByEmailSection({ inviteSubject, inviteText, inviteTextiOS, t }: IProps) {
const dispatch = useDispatch();
const { classes } = useStyles();
const { t } = useTranslation();
const [ isClicked, setIsClicked ] = useState(false);
const encodedInviteSubject = encodeURIComponent(inviteSubject);
const encodedInviteText = encodeURIComponent(inviteText);
const encodedInviteTextiOS = encodeURIComponent(inviteTextiOS);
const encodedDefaultEmailText = isIosMobileBrowser() ? encodedInviteTextiOS : encodedInviteText;
useEffect(() => {
mounted = true;
return () => {
mounted = false;
};
}, []);
/**
* Copies the conference invitation to the clipboard.
*
* @returns {void}
*/
function _onCopyText() {
copyText(inviteText);
dispatch(showSuccessNotification({
titleKey: 'dialog.copied'
}, NOTIFICATION_TIMEOUT_TYPE.SHORT));
copyText(inviteText);
setIsClicked(true);
setTimeout(() => {
// avoid: Can't perform a React state update on an unmounted component
if (mounted) {
setIsClicked(false);
}
}, 2500);
}
/**
@@ -95,10 +115,7 @@ function InviteByEmailSection({ inviteSubject, inviteText, inviteTextiOS }: IPro
function _onCopyTextKeyPress(e: React.KeyboardEvent) {
if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault();
dispatch(showSuccessNotification({
titleKey: 'dialog.copied'
}, NOTIFICATION_TIMEOUT_TYPE.SHORT));
copyText(inviteText);
_onCopyText();
}
}
@@ -174,7 +191,7 @@ function InviteByEmailSection({ inviteSubject, inviteText, inviteTextiOS }: IPro
onKeyPress = { _onCopyTextKeyPress }
role = 'button'
tabIndex = { 0 }>
<Icon src = { IconCopy } />
<Icon src = { isClicked ? IconCheck : IconCopy } />
</div>
</Tooltip>
{renderEmailIcons()}
@@ -184,4 +201,4 @@ function InviteByEmailSection({ inviteSubject, inviteText, inviteTextiOS }: IPro
);
}
export default InviteByEmailSection;
export default translate(InviteByEmailSection);

View File

@@ -83,7 +83,8 @@ class DialInSummary extends PureComponent<IProps> {
setSupportMultipleWindows = { false }
source = {{ uri: getDialInfoPageURLForURIString(summaryUrl) ?? '' }}
startInLoadingState = { true }
style = { styles.webView } />
style = { styles.webView }
webviewDebuggingEnabled = { true } />
</JitsiScreen>
);
}

View File

@@ -1,10 +1,10 @@
import React from 'react';
import { Image } from 'react-native';
import { SvgCssUri } from 'react-native-svg';
import { connect } from 'react-redux';
import { translate } from '../../../base/i18n/functions';
import AbstractButton, { IProps as AbstractButtonProps }
from '../../../base/toolbox/components/AbstractButton';
import AbstractButton, { IProps as AbstractButtonProps } from '../../../base/toolbox/components/AbstractButton';
import styles from './styles';
@@ -30,11 +30,30 @@ class CustomOptionButton extends AbstractButton<IProps> {
*
* @returns {React.Component}
*/
icon = () => (
<Image
source = {{ uri: this.iconSrc }}
style = { styles.iconImageStyles } />
);
icon = () => {
let iconComponent;
if (!this.iconSrc) {
return null;
}
if (this.iconSrc?.includes('svg')) {
iconComponent
= (
<SvgCssUri
style = { styles.iconImageStyles }
uri = { this.iconSrc } />);
} else {
iconComponent
= (
<Image
source = {{ uri: this.iconSrc }}
style = { styles.iconImageStyles }
tintColor = { 'white' } />);
}
return iconComponent;
};
label = this.text;
}

View File

@@ -106,8 +106,8 @@ const styles = {
},
iconImageStyles: {
height: BaseTheme.spacing[5],
width: BaseTheme.spacing[5]
height: BaseTheme.spacing[4],
width: BaseTheme.spacing[4]
}
};

View File

@@ -128,6 +128,7 @@ class Whiteboard extends PureComponent<IProps> {
safeAreaInsets = { [ 'bottom', 'left', 'right' ] }
style = { styles.backDrop }>
<WebView
domStorageEnabled = { false }
incognito = { true }
javaScriptEnabled = { true }
nestedScrollEnabled = { true }
@@ -139,7 +140,8 @@ class Whiteboard extends PureComponent<IProps> {
setSupportMultipleWindows = { false }
source = {{ uri }}
startInLoadingState = { true }
style = { styles.webView } />
style = { styles.webView }
webviewDebuggingEnabled = { true } />
</JitsiScreen>
);
}

View File

@@ -11,12 +11,14 @@ if not muc_domain_base then
return
end
local log_not_allowed_errors = module:get_option_boolean('muc_mapper_log_not_allowed_errors', false);
local util = module:require "util";
local room_jid_match_rewrite = util.room_jid_match_rewrite;
local internal_room_jid_match_rewrite = util.internal_room_jid_match_rewrite;
-- We must filter stanzas in order to hook in to all incoming and outgoing messaging which skips the stanza routers
function filter_stanza(stanza)
function filter_stanza(stanza, session)
if stanza.skipMapping then
return stanza;
end
@@ -36,6 +38,16 @@ function filter_stanza(stanza)
if stanza.attr.from then
stanza.attr.from = internal_room_jid_match_rewrite(stanza.attr.from, stanza)
end
if log_not_allowed_errors and stanza.name == 'presence' and stanza.attr.type == 'error' then
local error = stanza:get_child('error');
if error and error.attr.type == 'cancel'
and error:get_child('not-allowed', 'urn:ietf:params:xml:ns:xmpp-stanzas')
and not session.jitsi_not_allowed_logged then
session.jitsi_not_allowed_logged = true;
session.log('error', 'Not allowed presence %s', stanza);
end
end
end
return stanza;
end

View File

@@ -443,8 +443,8 @@ process_host_module(muc_domain_prefix..'.'..muc_domain_base, function(host_modul
process_promotion_response(room, data.id, data.approved and 'true' or 'false');
else
-- we are in the case with admit all, we need to read data.ids
for i in pairs(data.ids) do
process_promotion_response(room, data.id, data.approved and 'true' or 'false');
for _,value in pairs(data.ids) do
process_promotion_response(room, value, data.approved and 'true' or 'false');
end
end
end