mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-03 21:32:28 +00:00
Compare commits
40 Commits
android-sd
...
7981
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57bd074d00 | ||
|
|
843cdf05f5 | ||
|
|
ede8f7ece9 | ||
|
|
e5b736243d | ||
|
|
1d8a9c11c8 | ||
|
|
72d05bb969 | ||
|
|
2a5fc8cc4a | ||
|
|
aa9bd8c814 | ||
|
|
f279e634e4 | ||
|
|
3f7c8c204b | ||
|
|
375314cbbd | ||
|
|
62d0d25395 | ||
|
|
3047af4df6 | ||
|
|
6682b52a19 | ||
|
|
fee4151f83 | ||
|
|
25f2eb012e | ||
|
|
6976b45789 | ||
|
|
c69962675b | ||
|
|
2bb1d6dee3 | ||
|
|
43c1032b46 | ||
|
|
980153e299 | ||
|
|
d2b4043c7f | ||
|
|
ce727d87a8 | ||
|
|
b540452583 | ||
|
|
f995eb2698 | ||
|
|
393c78aad3 | ||
|
|
6452c998a2 | ||
|
|
038292305e | ||
|
|
aa04692e9b | ||
|
|
219e6ce1ca | ||
|
|
6b2d586aee | ||
|
|
28a9850111 | ||
|
|
d9ef9dc6a0 | ||
|
|
88b6cdf39b | ||
|
|
e3ab6c9f33 | ||
|
|
9bb27b83d9 | ||
|
|
d8b0710a19 | ||
|
|
b02b7ac769 | ||
|
|
b90e187a73 | ||
|
|
582bbf890b |
2
.github/workflows/ci-lua.yml
vendored
2
.github/workflows/ci-lua.yml
vendored
@@ -7,7 +7,7 @@ jobs:
|
||||
name: Luacheck
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install luarocks
|
||||
run: sudo apt-get --install-recommends -y install luarocks
|
||||
|
||||
36
.github/workflows/ci.yml
vendored
36
.github/workflows/ci.yml
vendored
@@ -7,8 +7,8 @@ jobs:
|
||||
name: Lint
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
cache: 'npm'
|
||||
@@ -34,8 +34,8 @@ jobs:
|
||||
name: Build Frontend (Linux)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
cache: 'npm'
|
||||
@@ -45,8 +45,8 @@ jobs:
|
||||
name: Build Frontend (macOS)
|
||||
runs-on: macOS-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
cache: 'npm'
|
||||
@@ -56,8 +56,8 @@ jobs:
|
||||
name: Build mobile bundle (Android)
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
cache: 'npm'
|
||||
@@ -65,14 +65,24 @@ jobs:
|
||||
- run: npx react-native bundle --entry-file react/index.native.js --platform android --bundle-output /tmp/android.bundle --reset-cache
|
||||
ios-build:
|
||||
name: Build mobile bundle (iOS)
|
||||
runs-on: macOS-latest
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [macos-13, macos-14]
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
cache: 'npm'
|
||||
- run: npm install
|
||||
- name: setup Xcode
|
||||
run: |
|
||||
uname -a
|
||||
xcode-select -p
|
||||
sudo xcode-select -s /Applications/Xcode_15.2.app/Contents/Developer
|
||||
xcodebuild -version
|
||||
- name: setup-cocoapods
|
||||
uses: maxim-lobanov/setup-cocoapods@v1
|
||||
with:
|
||||
@@ -89,8 +99,8 @@ jobs:
|
||||
name: Test Debian packages build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-node@v3
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 16
|
||||
cache: 'npm'
|
||||
|
||||
@@ -12,8 +12,10 @@
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
<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_MICROPHONE" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<uses-feature
|
||||
android:glEsVersion="0x00020000"
|
||||
@@ -49,7 +51,7 @@
|
||||
|
||||
<service
|
||||
android:name="org.jitsi.meet.sdk.JitsiMeetOngoingConferenceService"
|
||||
android:foregroundServiceType="mediaPlayback" />
|
||||
android:foregroundServiceType="mediaPlayback|microphone" />
|
||||
|
||||
<provider
|
||||
android:name="com.reactnativecommunity.webview.RNCWebViewFileProvider"
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import static android.Manifest.permission.POST_NOTIFICATIONS;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.Notification;
|
||||
import android.app.NotificationManager;
|
||||
@@ -24,6 +26,7 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ServiceInfo;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
@@ -31,6 +34,8 @@ import android.os.IBinder;
|
||||
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
|
||||
import com.facebook.react.modules.core.PermissionListener;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
import java.util.HashMap;
|
||||
@@ -52,12 +57,13 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
|
||||
private final BroadcastReceiver broadcastReceiver = new BroadcastReceiver();
|
||||
|
||||
private static final int POST_NOTIFICATIONS_PERMISSION_REQUEST_CODE = (int) (Math.random() * Short.MAX_VALUE);
|
||||
|
||||
private boolean isAudioMuted;
|
||||
|
||||
static final int NOTIFICATION_ID = new Random().nextInt(99999) + 10000;
|
||||
|
||||
|
||||
public static void launch(Context context, HashMap<String, Object> extraData) {
|
||||
private static void doLaunch(Context context, HashMap<String, Object> extraData) {
|
||||
|
||||
OngoingNotification.createNotificationChannel((Activity) context);
|
||||
|
||||
@@ -87,6 +93,31 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void launch(Context context, HashMap<String, Object> extraData) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
PermissionListener listener = new PermissionListener() {
|
||||
@Override
|
||||
public boolean onRequestPermissionsResult(int i, String[] strings, int[] results) {
|
||||
if (results.length > 0 && results[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
doLaunch(context, extraData);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
JitsiMeetActivityDelegate.requestPermissions(
|
||||
(Activity) context,
|
||||
new String[]{POST_NOTIFICATIONS},
|
||||
POST_NOTIFICATIONS_PERMISSION_REQUEST_CODE,
|
||||
listener
|
||||
);
|
||||
} else {
|
||||
doLaunch(context, extraData);
|
||||
}
|
||||
}
|
||||
|
||||
public static void abort(Context context) {
|
||||
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||
context.stopService(intent);
|
||||
@@ -102,7 +133,7 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
JitsiMeetLogger.w(TAG + " Couldn't start service, notification is null");
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK);
|
||||
startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK | ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE);
|
||||
} else {
|
||||
startForeground(NOTIFICATION_ID, notification);
|
||||
}
|
||||
|
||||
286
conference.js
286
conference.js
@@ -2,7 +2,6 @@
|
||||
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
import Logger from '@jitsi/logger';
|
||||
import EventEmitter from 'events';
|
||||
|
||||
import { ENDPOINT_TEXT_MESSAGE_NAME } from './modules/API/constants';
|
||||
import { AUDIO_ONLY_SCREEN_SHARE_NO_TRACK } from './modules/UI/UIErrors';
|
||||
@@ -18,7 +17,6 @@ import {
|
||||
import { sendAnalytics } from './react/features/analytics/functions';
|
||||
import {
|
||||
maybeRedirectToWelcomePage,
|
||||
redirectToStaticPage,
|
||||
reloadWithStoredParams
|
||||
} from './react/features/app/actions';
|
||||
import { showModeratedNotification } from './react/features/av-moderation/actions';
|
||||
@@ -56,7 +54,7 @@ import {
|
||||
getConferenceOptions,
|
||||
sendLocalParticipant
|
||||
} from './react/features/base/conference/functions';
|
||||
import { getReplaceParticipant } from './react/features/base/config/functions';
|
||||
import { getReplaceParticipant, getSsrcRewritingFeatureFlag } from './react/features/base/config/functions';
|
||||
import { connect } from './react/features/base/connection/actions.web';
|
||||
import {
|
||||
checkAndNotifyForNewDevice,
|
||||
@@ -165,12 +163,8 @@ import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/Au
|
||||
import { createRnnoiseProcessor } from './react/features/stream-effects/rnnoise';
|
||||
import { handleToggleVideoMuted } from './react/features/toolbox/actions.any';
|
||||
import { muteLocal } from './react/features/video-menu/actions.any';
|
||||
import UIEvents from './service/UI/UIEvents';
|
||||
|
||||
const logger = Logger.getLogger(__filename);
|
||||
|
||||
const eventEmitter = new EventEmitter();
|
||||
|
||||
let room;
|
||||
|
||||
/*
|
||||
@@ -280,12 +274,6 @@ class ConferenceConnector {
|
||||
|
||||
switch (err) {
|
||||
|
||||
case JitsiConferenceErrors.NOT_ALLOWED_ERROR: {
|
||||
// let's show some auth not allowed page
|
||||
APP.store.dispatch(redirectToStaticPage('static/authError.html'));
|
||||
break;
|
||||
}
|
||||
|
||||
case JitsiConferenceErrors.RESERVATION_ERROR: {
|
||||
const [ code, msg ] = params;
|
||||
|
||||
@@ -1916,21 +1904,12 @@ export default {
|
||||
JitsiE2ePingEvents.E2E_RTT_CHANGED,
|
||||
(...args) => APP.store.dispatch(e2eRttChanged(...args)));
|
||||
|
||||
APP.UI.addListener(UIEvents.AUDIO_MUTED, muted => {
|
||||
this.muteAudio(muted);
|
||||
});
|
||||
APP.UI.addListener(UIEvents.VIDEO_MUTED, (muted, showUI = false) => {
|
||||
this.muteVideo(muted, showUI);
|
||||
});
|
||||
|
||||
room.addCommandListener(this.commands.defaults.ETHERPAD,
|
||||
({ value }) => {
|
||||
APP.UI.initEtherpad(value);
|
||||
}
|
||||
);
|
||||
|
||||
APP.UI.addListener(UIEvents.EMAIL_CHANGED,
|
||||
this.changeLocalEmail.bind(this));
|
||||
room.addCommandListener(this.commands.defaults.EMAIL, (data, from) => {
|
||||
APP.store.dispatch(participantUpdated({
|
||||
conference: room,
|
||||
@@ -1950,9 +1929,6 @@ export default {
|
||||
}));
|
||||
});
|
||||
|
||||
APP.UI.addListener(UIEvents.NICKNAME_CHANGED,
|
||||
this.changeLocalDisplayName.bind(this));
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.START_MUTED_POLICY_CHANGED,
|
||||
({ audio, video }) => {
|
||||
@@ -1999,123 +1975,146 @@ export default {
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.DATA_CHANNEL_CLOSED, ev => {
|
||||
APP.store.dispatch(dataChannelClosed(ev.code, ev.reason));
|
||||
APP.store.dispatch(showWarningNotification({
|
||||
descriptionKey: 'notify.dataChannelClosedDescription',
|
||||
titleKey: 'notify.dataChannelClosed',
|
||||
uid: DATA_CHANNEL_CLOSED_NOTIFICATION_ID
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
|
||||
const state = APP.store.getState();
|
||||
const { dataChannelOpen } = state['features/base/conference'];
|
||||
const timeout = typeof dataChannelOpen === 'undefined' ? 15000 : 60000;
|
||||
|
||||
// Show the notification only when the data channel connection doesn't get re-established in 60 secs if
|
||||
// it was already established at the beginning of the call, show it sooner otherwise. This notification
|
||||
// can be confusing and alarming to users even when there is no significant impact to user experience
|
||||
// if the the reconnect happens immediately.
|
||||
setTimeout(() => {
|
||||
const { dataChannelOpen: open } = APP.store.getState()['features/base/conference'];
|
||||
|
||||
if (!open) {
|
||||
const descriptionKey = getSsrcRewritingFeatureFlag(state)
|
||||
? 'notify.dataChannelClosedDescriptionWithAudio' : 'notify.dataChannelClosedDescription';
|
||||
const titleKey = getSsrcRewritingFeatureFlag(state)
|
||||
? 'notify.dataChannelClosedWithAudio' : 'notify.dataChannelClosed';
|
||||
|
||||
APP.store.dispatch(dataChannelClosed(ev.code, ev.reason));
|
||||
APP.store.dispatch(showWarningNotification({
|
||||
descriptionKey,
|
||||
titleKey,
|
||||
uid: DATA_CHANNEL_CLOSED_NOTIFICATION_ID
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
|
||||
}
|
||||
}, timeout);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// call hangup
|
||||
APP.UI.addListener(UIEvents.HANGUP, () => {
|
||||
this.hangup(true);
|
||||
/**
|
||||
* Handles audio device changes.
|
||||
*
|
||||
* @param {string} cameraDeviceId - The new device id.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async onAudioDeviceChanged(micDeviceId) {
|
||||
const audioWasMuted = this.isLocalAudioMuted();
|
||||
|
||||
// Disable noise suppression if it was enabled on the previous track.
|
||||
await APP.store.dispatch(setNoiseSuppressionEnabled(false));
|
||||
|
||||
// When the 'default' mic needs to be selected, we need to pass the real device id to gUM instead of
|
||||
// 'default' in order to get the correct MediaStreamTrack from chrome because of the following bug.
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=997689.
|
||||
const isDefaultMicSelected = micDeviceId === 'default';
|
||||
const selectedDeviceId = isDefaultMicSelected
|
||||
? getDefaultDeviceId(APP.store.getState(), 'audioInput')
|
||||
: micDeviceId;
|
||||
|
||||
logger.info(`Switching audio input device to ${selectedDeviceId}`);
|
||||
sendAnalytics(createDeviceChangedEvent('audio', 'input'));
|
||||
createLocalTracksF({
|
||||
devices: [ 'audio' ],
|
||||
micDeviceId: selectedDeviceId
|
||||
})
|
||||
.then(([ stream ]) => {
|
||||
// if audio was muted before changing the device, mute
|
||||
// with the new device
|
||||
if (audioWasMuted) {
|
||||
return stream.mute()
|
||||
.then(() => stream);
|
||||
}
|
||||
|
||||
return stream;
|
||||
})
|
||||
.then(async stream => {
|
||||
await this._maybeApplyAudioMixerEffect(stream);
|
||||
|
||||
return this.useAudioStream(stream);
|
||||
})
|
||||
.then(() => {
|
||||
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
|
||||
|
||||
if (localAudio && isDefaultMicSelected) {
|
||||
// workaround for the default device to be shown as selected in the
|
||||
// settings even when the real device id was passed to gUM because of the
|
||||
// above mentioned chrome bug.
|
||||
localAudio._realDeviceId = localAudio.deviceId = 'default';
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(`Failed to switch to selected audio input device ${selectedDeviceId}, error=${err}`);
|
||||
APP.store.dispatch(notifyMicError(err));
|
||||
});
|
||||
},
|
||||
|
||||
APP.UI.addListener(
|
||||
UIEvents.VIDEO_DEVICE_CHANGED,
|
||||
cameraDeviceId => {
|
||||
const videoWasMuted = this.isLocalVideoMuted();
|
||||
const localVideoTrack = getLocalJitsiVideoTrack(APP.store.getState());
|
||||
/**
|
||||
* Handles video device changes.
|
||||
*
|
||||
* @param {string} cameraDeviceId - The new device id.
|
||||
* @returns {void}
|
||||
*/
|
||||
onVideoDeviceChanged(cameraDeviceId) {
|
||||
const videoWasMuted = this.isLocalVideoMuted();
|
||||
const localVideoTrack = getLocalJitsiVideoTrack(APP.store.getState());
|
||||
|
||||
if (localVideoTrack?.getDeviceId() === cameraDeviceId) {
|
||||
return;
|
||||
}
|
||||
if (localVideoTrack?.getDeviceId() === cameraDeviceId) {
|
||||
return;
|
||||
}
|
||||
|
||||
sendAnalytics(createDeviceChangedEvent('video', 'input'));
|
||||
sendAnalytics(createDeviceChangedEvent('video', 'input'));
|
||||
|
||||
createLocalTracksF({
|
||||
devices: [ 'video' ],
|
||||
cameraDeviceId
|
||||
})
|
||||
.then(([ stream ]) => {
|
||||
// if we are in audio only mode or video was muted before
|
||||
// changing device, then mute
|
||||
if (this.isAudioOnly() || videoWasMuted) {
|
||||
return stream.mute()
|
||||
.then(() => stream);
|
||||
}
|
||||
|
||||
return stream;
|
||||
})
|
||||
.then(stream => {
|
||||
logger.info(`Switching the local video device to ${cameraDeviceId}.`);
|
||||
|
||||
return this.useVideoStream(stream);
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error(`Failed to switch to selected camera:${cameraDeviceId}, error:${error}`);
|
||||
|
||||
return APP.store.dispatch(notifyCameraError(error));
|
||||
});
|
||||
createLocalTracksF({
|
||||
devices: [ 'video' ],
|
||||
cameraDeviceId
|
||||
})
|
||||
.then(([ stream ]) => {
|
||||
// if we are in audio only mode or video was muted before
|
||||
// changing device, then mute
|
||||
if (this.isAudioOnly() || videoWasMuted) {
|
||||
return stream.mute()
|
||||
.then(() => stream);
|
||||
}
|
||||
);
|
||||
|
||||
APP.UI.addListener(
|
||||
UIEvents.AUDIO_DEVICE_CHANGED,
|
||||
async micDeviceId => {
|
||||
const audioWasMuted = this.isLocalAudioMuted();
|
||||
return stream;
|
||||
})
|
||||
.then(stream => {
|
||||
logger.info(`Switching the local video device to ${cameraDeviceId}.`);
|
||||
|
||||
// Disable noise suppression if it was enabled on the previous track.
|
||||
await APP.store.dispatch(setNoiseSuppressionEnabled(false));
|
||||
return this.useVideoStream(stream);
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error(`Failed to switch to selected camera:${cameraDeviceId}, error:${error}`);
|
||||
|
||||
// When the 'default' mic needs to be selected, we need to pass the real device id to gUM instead of
|
||||
// 'default' in order to get the correct MediaStreamTrack from chrome because of the following bug.
|
||||
// https://bugs.chromium.org/p/chromium/issues/detail?id=997689.
|
||||
const isDefaultMicSelected = micDeviceId === 'default';
|
||||
const selectedDeviceId = isDefaultMicSelected
|
||||
? getDefaultDeviceId(APP.store.getState(), 'audioInput')
|
||||
: micDeviceId;
|
||||
|
||||
logger.info(`Switching audio input device to ${selectedDeviceId}`);
|
||||
sendAnalytics(createDeviceChangedEvent('audio', 'input'));
|
||||
createLocalTracksF({
|
||||
devices: [ 'audio' ],
|
||||
micDeviceId: selectedDeviceId
|
||||
})
|
||||
.then(([ stream ]) => {
|
||||
// if audio was muted before changing the device, mute
|
||||
// with the new device
|
||||
if (audioWasMuted) {
|
||||
return stream.mute()
|
||||
.then(() => stream);
|
||||
}
|
||||
|
||||
return stream;
|
||||
})
|
||||
.then(async stream => {
|
||||
await this._maybeApplyAudioMixerEffect(stream);
|
||||
|
||||
return this.useAudioStream(stream);
|
||||
})
|
||||
.then(() => {
|
||||
const localAudio = getLocalJitsiAudioTrack(APP.store.getState());
|
||||
|
||||
if (localAudio && isDefaultMicSelected) {
|
||||
// workaround for the default device to be shown as selected in the
|
||||
// settings even when the real device id was passed to gUM because of the
|
||||
// above mentioned chrome bug.
|
||||
localAudio._realDeviceId = localAudio.deviceId = 'default';
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
logger.error(`Failed to switch to selected audio input device ${selectedDeviceId}, error=${err}`);
|
||||
APP.store.dispatch(notifyMicError(err));
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
APP.UI.addListener(UIEvents.TOGGLE_AUDIO_ONLY, () => {
|
||||
// Immediately update the UI by having remote videos and the large video update themselves.
|
||||
const displayedUserId = APP.UI.getLargeVideoID();
|
||||
|
||||
if (displayedUserId) {
|
||||
APP.UI.updateLargeVideo(displayedUserId, true);
|
||||
}
|
||||
return APP.store.dispatch(notifyCameraError(error));
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Handles audio only changes.
|
||||
*/
|
||||
onToggleAudioOnly() {
|
||||
// Immediately update the UI by having remote videos and the large video update themselves.
|
||||
const displayedUserId = APP.UI.getLargeVideoID();
|
||||
|
||||
if (displayedUserId) {
|
||||
APP.UI.updateLargeVideo(displayedUserId, true);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Cleanups local conference on suspend.
|
||||
*/
|
||||
@@ -2408,8 +2407,6 @@ export default {
|
||||
this.deviceChangeListener);
|
||||
}
|
||||
|
||||
APP.UI.removeAllListeners();
|
||||
|
||||
let feedbackResultPromise = Promise.resolve({});
|
||||
|
||||
if (requestFeedback) {
|
||||
@@ -2516,37 +2513,6 @@ export default {
|
||||
room.sendEndpointMessage(to, payload);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds new listener.
|
||||
* @param {String} eventName the name of the event
|
||||
* @param {Function} listener the listener.
|
||||
*/
|
||||
addListener(eventName, listener) {
|
||||
eventEmitter.addListener(eventName, listener);
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes listener.
|
||||
* @param {String} eventName the name of the event that triggers the
|
||||
* listener
|
||||
* @param {Function} listener the listener.
|
||||
*/
|
||||
removeListener(eventName, listener) {
|
||||
eventEmitter.removeListener(eventName, listener);
|
||||
},
|
||||
|
||||
/**
|
||||
* Changes the display name for the local user
|
||||
* @param nickname {string} the new display name
|
||||
*/
|
||||
changeLocalDisplayName(nickname = '') {
|
||||
const formattedNickname = getNormalizedDisplayName(nickname);
|
||||
|
||||
APP.store.dispatch(updateSettings({
|
||||
displayName: formattedNickname
|
||||
}));
|
||||
},
|
||||
|
||||
/**
|
||||
* Callback invoked by the external api create or update a direct connection
|
||||
* from the local client to an external client.
|
||||
|
||||
@@ -467,6 +467,8 @@ var config = {
|
||||
// low: 100000,
|
||||
// standard: 300000,
|
||||
// high: 1000000,
|
||||
// fullHd: 2000000,
|
||||
// ultraHd: 4000000,
|
||||
// ssHigh: 2500000
|
||||
// },
|
||||
// scalabilityModeEnabled: true,
|
||||
@@ -478,6 +480,8 @@ var config = {
|
||||
// low: 200000,
|
||||
// standard: 500000,
|
||||
// high: 1500000,
|
||||
// fullHd: 3000000,
|
||||
// ultraHd: 6000000,
|
||||
// ssHigh: 2500000
|
||||
// },
|
||||
// scalabilityModeEnabled: true
|
||||
@@ -487,6 +491,8 @@ var config = {
|
||||
// low: 200000,
|
||||
// standard: 500000,
|
||||
// high: 1500000,
|
||||
// fullHd: 3000000,
|
||||
// ultraHd: 6000000,
|
||||
// ssHigh: 2500000
|
||||
// },
|
||||
// scalabilityModeEnabled: false
|
||||
@@ -496,6 +502,8 @@ var config = {
|
||||
// low: 100000,
|
||||
// standard: 300000,
|
||||
// high: 1200000,
|
||||
// fullHd: 2500000,
|
||||
// ultraHd: 5000000,
|
||||
// ssHigh: 2500000
|
||||
// },
|
||||
// scalabilityModeEnabled: true,
|
||||
|
||||
6
debian/jitsi-meet-prosody.postinst
vendored
6
debian/jitsi-meet-prosody.postinst
vendored
@@ -138,10 +138,8 @@ case "$1" in
|
||||
PROSODY_CONFIG_PRESENT="false"
|
||||
fi
|
||||
|
||||
USER_EXISTS_CHECK=`prosodyctl adduser jvb@$JICOFO_AUTH_DOMAIN < /dev/null || true`
|
||||
if [ ! "$USER_EXISTS_CHECK" = "That user already exists" ]; then
|
||||
prosodyctl register jvb $JICOFO_AUTH_DOMAIN $JVB_SECRET || true
|
||||
fi
|
||||
# creates the user if it does not exist
|
||||
echo -e "$JVB_SECRET\n$JVB_SECRET" | prosodyctl adduser jvb@$JICOFO_AUTH_DOMAIN > /dev/null || true
|
||||
|
||||
# Check whether prosody config has the internal muc, if not add it,
|
||||
# as we are migrating configs
|
||||
|
||||
@@ -456,7 +456,8 @@ PODS:
|
||||
- react-native-webrtc (118.0.7):
|
||||
- JitsiWebRTC (~> 118.0.0)
|
||||
- React-Core
|
||||
- react-native-webview (13.5.1):
|
||||
- react-native-webview (13.8.7):
|
||||
- RCT-Folly (= 2021.07.22.00)
|
||||
- React-Core
|
||||
- React-NativeModulesApple (0.72.9):
|
||||
- React-callinvoker
|
||||
@@ -882,7 +883,7 @@ SPEC CHECKSUMS:
|
||||
react-native-splash-screen: 4312f786b13a81b5169ef346d76d33bc0c6dc457
|
||||
react-native-video: 472b7c366eaaaa0207e546d9a50410df89790bcf
|
||||
react-native-webrtc: 8b024c7bb9a005d2b9efeba4c691172dbd00268d
|
||||
react-native-webview: 8baa0f5c6d336d6ba488e942bcadea5bf51f050a
|
||||
react-native-webview: fb0df3f6551616ac5a7ccb2bc77aca451f58373d
|
||||
React-NativeModulesApple: 4225ac31a26696c02c54b471052b3e85e74a9a0c
|
||||
React-perflogger: cb433f318c6667060fc1f62e26eb58d6eb30a627
|
||||
React-RCTActionSheet: 0af3f8ac067e8a1dde902810b7ad169d0a0ec31e
|
||||
|
||||
@@ -367,13 +367,12 @@
|
||||
13B07F8C1A680F5B00A75B9A /* Frameworks */,
|
||||
13B07F8E1A680F5B00A75B9A /* Resources */,
|
||||
0B26BE701EC5BC3C00EEFB41 /* Embed Frameworks */,
|
||||
0BB7DA181EC9E695007AAE98 /* Adjust ATS */,
|
||||
DEF4813D224925A2002AD03A /* Copy Google Plist file */,
|
||||
DE11877A21EE09640078D059 /* Setup Google reverse URL handler */,
|
||||
DE4F6D6E22005C0400DE699E /* Setup Dropbox */,
|
||||
4E81688528A408E600F8FA9E /* Update App Entitlements */,
|
||||
0BEA5C491F7B8F73000D0AB4 /* Embed Watch Content */,
|
||||
4EC49B9025BED71300E76218 /* Embed App Extensions */,
|
||||
4E81688528A408E600F8FA9E /* Update App Entitlements */,
|
||||
DE11877A21EE09640078D059 /* Setup Google reverse URL handler */,
|
||||
0BB7DA181EC9E695007AAE98 /* Adjust ATS */,
|
||||
DE4F6D6E22005C0400DE699E /* Setup Dropbox */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
@@ -499,10 +498,12 @@
|
||||
/* Begin PBXShellScriptBuildPhase section */
|
||||
0BB7DA181EC9E695007AAE98 /* Adjust ATS */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH",
|
||||
);
|
||||
name = "Adjust ATS";
|
||||
outputPaths = (
|
||||
@@ -513,6 +514,7 @@
|
||||
};
|
||||
0BBA83C41EC9F7600075A103 /* Run React packager */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
@@ -527,12 +529,14 @@
|
||||
};
|
||||
4E81688528A408E600F8FA9E /* Update App Entitlements */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$PROJECT_DIR/app.entitlements",
|
||||
);
|
||||
name = "Update App Entitlements";
|
||||
outputFileListPaths = (
|
||||
@@ -567,12 +571,14 @@
|
||||
};
|
||||
DE11877A21EE09640078D059 /* Setup Google reverse URL handler */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH",
|
||||
);
|
||||
name = "Setup Google reverse URL handler";
|
||||
outputFileListPaths = (
|
||||
@@ -581,16 +587,18 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "INFO_PLIST=\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"\nGOOGLE_PLIST=\"$PROJECT_DIR/GoogleService-Info.plist\"\n\nif [[ -f $GOOGLE_PLIST ]]; then\n REVERSED_CLIENT_ID=$(/usr/libexec/PlistBuddy -c \"Print :REVERSED_CLIENT_ID:\" $GOOGLE_PLIST)\n /usr/libexec/PlistBuddy -c \"Set :CFBundleURLTypes:1:CFBundleURLSchemes:0 $REVERSED_CLIENT_ID\" $INFO_PLIST\nfi\n";
|
||||
shellScript = "GOOGLE_PLIST_NAME=\"GoogleService-Info.plist\"\nGOOGLE_PLIST=\"$PROJECT_DIR/$GOOGLE_PLIST_NAME\"\nBUILD_APP_DIR=\"$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.app\"\nINFO_PLIST=\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"\n\nif [[ -f $GOOGLE_PLIST ]]; then\n cp $GOOGLE_PLIST \"$BUILD_APP_DIR/$GOOGLE_PLIST_NAME\"\n REVERSED_CLIENT_ID=$(/usr/libexec/PlistBuddy -c \"Print :REVERSED_CLIENT_ID:\" $GOOGLE_PLIST)\n /usr/libexec/PlistBuddy -c \"Set :CFBundleURLTypes:1:CFBundleURLSchemes:0 $REVERSED_CLIENT_ID\" $INFO_PLIST\nfi\n";
|
||||
};
|
||||
DE4F6D6E22005C0400DE699E /* Setup Dropbox */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
alwaysOutOfDate = 1;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH",
|
||||
);
|
||||
name = "Setup Dropbox";
|
||||
outputFileListPaths = (
|
||||
@@ -601,24 +609,6 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "INFO_PLIST=\"$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH\"\nDROPBOX_KEY_FILE=\"$PROJECT_DIR/dropbox.key\"\n\nif [[ -f $DROPBOX_KEY_FILE ]]; then\n /usr/libexec/PlistBuddy -c \"Delete :LSApplicationQueriesSchemes\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :LSApplicationQueriesSchemes array\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :LSApplicationQueriesSchemes:0 string 'dbapi-2'\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :LSApplicationQueriesSchemes:1 string 'dbapi-8-emm'\" $INFO_PLIST\n\n DROPBOX_KEY=$(head -n 1 $DROPBOX_KEY_FILE)\n /usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:2:CFBundleURLName string dropbox\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:2:CFBundleURLSchemes array\" $INFO_PLIST\n /usr/libexec/PlistBuddy -c \"Add :CFBundleURLTypes:2:CFBundleURLSchemes:0 string $DROPBOX_KEY\" $INFO_PLIST\nfi\n";
|
||||
};
|
||||
DEF4813D224925A2002AD03A /* Copy Google Plist file */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = "Copy Google Plist file";
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "GOOGLE_PLIST_NAME=\"GoogleService-Info.plist\"\nGOOGLE_PLIST=\"$PROJECT_DIR/$GOOGLE_PLIST_NAME\"\nBUILD_APP_DIR=\"$BUILT_PRODUCTS_DIR/$PRODUCT_NAME.app\"\n\nif [[ -f $GOOGLE_PLIST ]]; then\n cp $GOOGLE_PLIST \"$BUILD_APP_DIR/$GOOGLE_PLIST_NAME\"\nfi\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@@ -1031,7 +1021,8 @@
|
||||
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
"-Wl",
|
||||
"-ld_classic",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
@@ -1094,7 +1085,8 @@
|
||||
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
"-Wl",
|
||||
"-ld_classic",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
|
||||
@@ -781,7 +781,8 @@
|
||||
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
"-Wl",
|
||||
"-ld_classic",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
@@ -847,7 +848,8 @@
|
||||
OTHER_CPLUSPLUSFLAGS = "$(inherited)";
|
||||
OTHER_LDFLAGS = (
|
||||
"$(inherited)",
|
||||
" ",
|
||||
"-Wl",
|
||||
"-ld_classic",
|
||||
);
|
||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||
SDKROOT = iphoneos;
|
||||
|
||||
@@ -128,6 +128,7 @@
|
||||
"privateNotice": "Private Nachricht an {{recipient}}",
|
||||
"sendButton": "Senden",
|
||||
"smileysPanel": "Emoji-Auswahl",
|
||||
"systemDisplayName": "System",
|
||||
"tabs": {
|
||||
"chat": "Chatten",
|
||||
"polls": "Umfragen"
|
||||
@@ -305,6 +306,8 @@
|
||||
"contactSupport": "Support kontaktieren",
|
||||
"copied": "Kopiert",
|
||||
"copy": "Kopieren",
|
||||
"demoteParticipantDialog": "Sind Sie sicher, dass Sie diese Person zu den Gästen verschieben möchten?",
|
||||
"demoteParticipantTitle": "Zu Gästen verschieben",
|
||||
"dismiss": "OK",
|
||||
"displayNameRequired": "Hallo! Wie ist Ihr Name?",
|
||||
"done": "Fertig",
|
||||
@@ -316,6 +319,7 @@
|
||||
"embedMeeting": "Besprechung einbetten",
|
||||
"enterDisplayName": "Bitte geben Sie hier Ihren Namen ein",
|
||||
"error": "Fehler",
|
||||
"errorRoomCreationRestriction": "Sie haben versucht, zu schnell beizutreten, bitte versuchen Sie es gleich noch einmal.",
|
||||
"gracefulShutdown": "Der Dienst steht momentan wegen Wartungsarbeiten nicht zur Verfügung. Bitte versuchen Sie es später noch einmal.",
|
||||
"grantModeratorDialog": "Möchten Sie wirklich Moderationsrechte an diese Person vergeben?",
|
||||
"grantModeratorTitle": "Moderationsrechte vergeben",
|
||||
@@ -560,6 +564,7 @@
|
||||
"noNumbers": "Keine Telefonnummern verfügbar.",
|
||||
"noPassword": "Kein Passwort benötigt",
|
||||
"noRoom": "Keine Konferenz für die Einwahlinformationen angegeben.",
|
||||
"noWhiteboard": "Whiteboard konnte nicht geladen werden.",
|
||||
"numbers": "Einwahlnummern",
|
||||
"password": "$t(lockRoomPasswordUppercase):",
|
||||
"reachedLimit": "Sie haben die Grenzen Ihres Tarifs erreicht.",
|
||||
@@ -567,7 +572,8 @@
|
||||
"sipAudioOnly": "SIP-Adresse (nur Ton)",
|
||||
"title": "Teilen",
|
||||
"tooltip": "Freigabe-Link und Einwahlinformationen für dieses Meeting",
|
||||
"upgradeOptions": "Bitte prüfen Sie Ihre Upgrade-Optionen auf"
|
||||
"upgradeOptions": "Bitte prüfen Sie Ihre Upgrade-Optionen auf",
|
||||
"whiteboardError": "Whiteboard konnte nicht geladen werden. Bitte versuchen Sie es später erneut."
|
||||
},
|
||||
"inlineDialogFailure": {
|
||||
"msg": "Es ist ein Fehler aufgetreten.",
|
||||
@@ -731,6 +737,8 @@
|
||||
"connectedTwoMembers": "{{first}} und {{second}} nehmen am Meeting teil",
|
||||
"dataChannelClosed": "Schlechte Videoqualität",
|
||||
"dataChannelClosedDescription": "Die Steuerungsverbindung (Bridge Channel) wurde unterbrochen, daher ist die Videoqulität auf die schlechteste Stufe limitiert.",
|
||||
"dataChannelClosedDescriptionWithAudio": "Die Steuerungsverbindung (Bridge Channel) wurde unterbrochen, daher können Video- und Tonprobleme auftreten.",
|
||||
"dataChannelClosedWithAudio": "Ton- und Videoqualität können beeinträchtigt sein",
|
||||
"disabledIframe": "Die Einbettung ist nur für Demo-Zwecke vorgesehen. Diese Konferenz wird in {{timeout}} Minuten beendet.",
|
||||
"disabledIframeSecondary": "Die Einbettung von {{domain}} ist nur für Demo-Zwecke vorgesehen. Diese Konferenz wird in {{timeout}} Minuten beendet. Bitte nutzen Sie <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> für produktive Zwecke!",
|
||||
"disconnected": "getrennt",
|
||||
@@ -802,12 +810,16 @@
|
||||
"startSilentTitle": "Sie sind ohne Audioausgabe beigetreten!",
|
||||
"suboptimalBrowserWarning": "Tut uns leid, aber die Konferenz wird mit {{appName}} kein großartiges Erlebnis. Wir versuchen immer die Situation zu verbessern, bis dahin empfehlen wir aber die Verwendung einer der <a href=\"{{recommendedBrowserPageLink}}\" target=\"_blank\">vollständig unterstützen Browser</a>.",
|
||||
"suboptimalExperienceTitle": "Browserwarnung",
|
||||
"suggestRecordingAction": "Starten",
|
||||
"suggestRecordingDescription": "Möchten Sie eine Aufzeichnung starten?",
|
||||
"suggestRecordingTitle": "Konferenz aufzeichnen",
|
||||
"unmute": "Stummschaltung aufheben",
|
||||
"videoMutedRemotelyDescription": "Sie können sie jederzeit wieder einschalten.",
|
||||
"videoMutedRemotelyTitle": "Ihre Kamera wurde von {{participantDisplayName}} ausgeschaltet!",
|
||||
"videoUnmuteBlockedDescription": "Die Kamera und Bildschirmfreigabe kann aus Überlastungsschutzgründen temporär nicht eingeschaltet werden.",
|
||||
"videoUnmuteBlockedTitle": "Kamera und Bildschirmfreigabe kann nicht aktiviert werden!",
|
||||
"viewLobby": "Lobby ansehen",
|
||||
"viewVisitors": "Gäste anzeigen",
|
||||
"waitingParticipants": "{{waitingParticipants}} Personen",
|
||||
"whiteboardLimitDescription": "Bitte speichern Sie Ihre Inhalte, da das Nutzungslimit bald erreicht wird und dann Ihr Whiteboard geschlossen wird.",
|
||||
"whiteboardLimitTitle": "Whiteboard-Nutzung"
|
||||
@@ -937,6 +949,7 @@
|
||||
"or": "oder",
|
||||
"premeeting": "Vorschau",
|
||||
"proceedAnyway": "Trotzdem fortsetzen",
|
||||
"recordingWarning": "Diese Konferenz wird möglicherweise von anderen Personen aufgezeichnet",
|
||||
"screenSharingError": "Fehler bei Bildschirmfreigabe:",
|
||||
"showScreen": "Konferenzvorschau aktivieren",
|
||||
"startWithPhone": "Mit Telefonaudio starten",
|
||||
@@ -1003,7 +1016,6 @@
|
||||
"limitNotificationDescriptionNative": "Wegen hoher Nachfrage ist Ihre Aufnahme auf {{limit}} Min. begrenzt. Für unlimitierte Aufnahmen nutzen Sie bitte <3>{{app}}</3>.",
|
||||
"limitNotificationDescriptionWeb": "Wegen hoher Nachfrage ist Ihre Aufnahme auf {{limit}} Min. begrenzt. Für unlimitierte Aufnahmen nutzen Sie bitte <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
|
||||
"linkGenerated": "Link zur Aufzeichnung wurde generiert.",
|
||||
"live": "LIVE",
|
||||
"localRecordingNoNotificationWarning": "Die Aufzeichnung wird anderen Anwesenden nicht mitgeteilt. Sie müssen diese selbst darauf hinweisen, dass die Konferenz aufgezeichnet wird.",
|
||||
"localRecordingNoVideo": "Videos werden nicht aufgenommen",
|
||||
"localRecordingStartWarning": "Bitte beenden Sie die Aufzeichnung vor dem Verlassen der Konferenz, um die Aufzeichnung zu speichern.",
|
||||
@@ -1020,7 +1032,6 @@
|
||||
"onBy": "{{name}} startete die Aufnahme",
|
||||
"onlyRecordSelf": "Nur eigenes Kamerabild und Ton aufzeichnen",
|
||||
"pending": "Aufzeichnung des Meetings wird vorbereitet…",
|
||||
"rec": "AUFZ",
|
||||
"recordAudioAndVideo": "Kamera und Ton aufzeichnen",
|
||||
"recordTranscription": "Transkription aufzeichnen",
|
||||
"saveLocalRecording": "Aufzeichnung lokal abspeichern",
|
||||
@@ -1177,7 +1188,7 @@
|
||||
"audioOnly": "„Nur Audio“ ein-/ausschalten",
|
||||
"audioRoute": "Audiogerät auswählen",
|
||||
"boo": "Buhen",
|
||||
"breakoutRoom": "Breakout-Räume betreten/verlassen",
|
||||
"breakoutRooms": "Breakout-Räume",
|
||||
"callQuality": "Qualitätseinstellungen",
|
||||
"carmode": "Automodus",
|
||||
"cc": "Untertitel ein-/ausschalten",
|
||||
@@ -1360,13 +1371,9 @@
|
||||
},
|
||||
"transcribing": {
|
||||
"ccButtonTooltip": "Untertitel ein-/ausschalten",
|
||||
"error": "Die Aufzeichnung ist fehlgeschlagen. Bitte versuchen Sie es erneut.",
|
||||
"expandedLabel": "Transkribieren ist derzeit eingeschaltet",
|
||||
"failedToStart": "Transkribieren konnte nicht gestartet werden",
|
||||
"labelToolTip": "Das Meeting wird transkribiert",
|
||||
"off": "Transkribieren gestoppt",
|
||||
"on": "Transkribieren gestartet",
|
||||
"pending": "Transkribieren des Meetings wird vorbereitet…",
|
||||
"sourceLanguageDesc": "Aktuell ist die Sprache der Konferenz auf <b>{{sourceLanguage}}</b> eingestellt. <br/> Sie könne dies hier ",
|
||||
"sourceLanguageHere": "ändern",
|
||||
"start": "Anzeige der Untertitel starten",
|
||||
@@ -1422,6 +1429,7 @@
|
||||
},
|
||||
"videothumbnail": {
|
||||
"connectionInfo": "Verbindungsinformationen",
|
||||
"demote": "Zu Gästen verschieben",
|
||||
"domute": "Stummschalten",
|
||||
"domuteOthers": "Alle anderen stummschalten",
|
||||
"domuteVideo": "Kamera ausschalten",
|
||||
@@ -1476,7 +1484,12 @@
|
||||
"chatIndicator": "(Gast)",
|
||||
"labelTooltip": "Anzahl Gäste: {{count}}",
|
||||
"notification": {
|
||||
"demoteDescription": "Hierhin verschoben von {{actor}}, bitte melden Sie sich um teilzunehmen",
|
||||
"description": "Bitte melden Sie sich um teilzunehmen",
|
||||
"noMainParticipantsDescription": "Eine Person muss die Konferenz starten. Bitte versuchen Sie es gleich noch einmal.",
|
||||
"noMainParticipantsTitle": "Diese Konferenz wurde noch nicht gestartet.",
|
||||
"noVisitorLobby": "Sie können nicht teilnehmen, solange die Lobby für diese Konferenz aktiviert ist.",
|
||||
"notAllowedPromotion": "Eine Person muss Ihre Anfrage erst erlauben.",
|
||||
"title": "Sie sind Gast in der Konferenz"
|
||||
}
|
||||
},
|
||||
@@ -1536,6 +1549,7 @@
|
||||
"whiteboard": {
|
||||
"accessibilityLabel": {
|
||||
"heading": "Whiteboard"
|
||||
}
|
||||
},
|
||||
"screenTitle": "Whiteboard"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@
|
||||
"mainRoom": "Salle principale",
|
||||
"notifications": {
|
||||
"joined": "Entrée en salle annexe \"{{name}}\"",
|
||||
"joinedMainRoom": "Retour à la salle principalem",
|
||||
"joinedMainRoom": "Retour à la salle principale",
|
||||
"joinedTitle": "Salles annexes"
|
||||
},
|
||||
"showParticipantList": "Afficher la liste des participants",
|
||||
@@ -82,7 +82,7 @@
|
||||
},
|
||||
"calendarSync": {
|
||||
"addMeetingURL": "Ajouter un lien de conférence",
|
||||
"confirmAddLink": "Voulez-vous ajouter un lien Jitsi à cet événement ?",
|
||||
"confirmAddLink": "Voulez-vous ajouter un lien Jitsi à cet événement?",
|
||||
"error": {
|
||||
"appConfiguration": "L'intégration du calendrier n'est pas correctement configurée.",
|
||||
"generic": "Une erreur s'est produite. Veuillez vérifier les paramètres de votre calendrier ou tenter de l'actualiser.",
|
||||
@@ -138,7 +138,7 @@
|
||||
},
|
||||
"chromeExtensionBanner": {
|
||||
"buttonText": "Installer l'extension Chrome",
|
||||
"buttonTextEdge": "Installer l’extension Edge",
|
||||
"buttonTextEdge": "Installer l'extension Edge",
|
||||
"close": "Fermer",
|
||||
"dontShowAgain": "Ne plus m'afficher ceci",
|
||||
"installExtensionText": "Installer l'extension pour l'intégration de Google Calendar et Office 365"
|
||||
@@ -424,7 +424,7 @@
|
||||
"shareAudioAltText": "Pour partager le contenu voulu, naviguer vers \"Onglet du Navigateur\", sélectionner le contenu, activer le bouton \"partager l’audio\" et enfin cliquer sur le bouton \"partager\"",
|
||||
"shareAudioTitle": "Comment partager le son",
|
||||
"shareAudioWarningD1": "vous devez cesser le partage d'écran avant de partager votre son.",
|
||||
"shareAudioWarningD2": "viys devez partager votre écran à nouveau et cocher l'ootion \"Partager l'audio\".",
|
||||
"shareAudioWarningD2": "vous devez partager votre écran à nouveau et cocher l'option \"Partager l'audio\".",
|
||||
"shareAudioWarningH1": "Si vous voulez partager uniquement de l'audio:",
|
||||
"shareAudioWarningTitle": "Vous devez cesser de partager l'écran avant de partager l'audio",
|
||||
"shareMediaWarningGenericH2": "Si vous voulez partager votre écran et l'audio",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -128,6 +128,7 @@
|
||||
"privateNotice": "Privāta ziņa adresātam {{recipient}}",
|
||||
"sendButton": "Nosūtīt",
|
||||
"smileysPanel": "Emociju panelis",
|
||||
"systemDisplayName": "Sistēma",
|
||||
"tabs": {
|
||||
"chat": "Tērzēšana",
|
||||
"polls": "Aptaujas"
|
||||
@@ -318,6 +319,7 @@
|
||||
"embedMeeting": "Iegult sapulci",
|
||||
"enterDisplayName": "Ievadiet savu vārdu",
|
||||
"error": "Kļūda",
|
||||
"errorRoomCreationRestriction": "Jūs mēģinājāt pievienoties pārāk ātri. Lūdzu, atgriezieties vēlāk.",
|
||||
"gracefulShutdown": "Mūsu serviss pašlaik nedarbojas apkopes dēļ. Lūdzu, pamēģiniet vēlreiz vēlāk.",
|
||||
"grantModeratorDialog": "Vai tiešām vēlaties piešķirt moderatora tiesības dalībniekam {{participantName}}?",
|
||||
"grantModeratorTitle": "Piešķirt moderatora tiesības",
|
||||
@@ -353,28 +355,28 @@
|
||||
"micPermissionDeniedError": "Nav piekļuves mikrofonam. Jūs varat piedalīties sapulcē, bet citi jūs nedzirdēs. Lai to novērstu, izmantojiet kameras ikonu pārlūkprogrammas adrešu joslā.",
|
||||
"micTimeoutError": "Nevarēja palaist audio avotu. Iestājās noildze!",
|
||||
"micUnknownError": "Nevar izmantot mikrofonu nezināma iemesla dēļ.",
|
||||
"moderationAudioLabel": "Ļaujiet dalībniekiem ieslēgt savu mikrofonu",
|
||||
"moderationVideoLabel": "Ļaujiet dalībniekiem ieslēgt savu kameru",
|
||||
"muteEveryoneDialog": "Dalībnieki jebkurā laikā var ieslēgt savu mikrofonu.",
|
||||
"muteEveryoneDialogModerationOn": "Dalībnieki jebkurā laikā var nosūtīt pieprasījumu runāt.",
|
||||
"muteEveryoneElseDialog": "Kad skaņa būs izslēgta, jūs nevarēsit to ieslēgt atpakaļ, taču dalībnieki jebkurā laikā to varēs izdarīt paši.",
|
||||
"moderationAudioLabel": "Atļaut dalībniekiem ieslēgt savu mikrofonu",
|
||||
"moderationVideoLabel": "Atļaut dalībniekiem ieslēgt savu kameru",
|
||||
"muteEveryoneDialog": "Dalībnieki paši var ieslēgt savu mikrofonu.",
|
||||
"muteEveryoneDialogModerationOn": "Dalībnieki var nosūtīt pieprasījumu ieslēgt savu mikrofonu.",
|
||||
"muteEveryoneElseDialog": "Kad skaņa būs izslēgta, jūs nevarēsiet to ieslēgt atpakaļ, taču dalībnieki to varēs izdarīt paši.",
|
||||
"muteEveryoneElseTitle": "Vai izslēgt skaņu visiem, izņemot {{whom}}?",
|
||||
"muteEveryoneElsesVideoDialog": "Kad video būs izslēgts, jūs nevarēsit to ieslēgt atpakaļ, taču dalībnieki jebkurā laikā to varēs izdarīt paši.",
|
||||
"muteEveryoneElsesVideoDialog": "Kad video būs izslēgts, jūs nevarēsiet to ieslēgt atpakaļ, taču dalībnieki to varēs izdarīt paši.",
|
||||
"muteEveryoneElsesVideoTitle": "Vai izslēgt video visiem, izņemot {{whom}}?",
|
||||
"muteEveryoneSelf": "jūs",
|
||||
"muteEveryoneStartMuted": "No šī brīža visi jauni dalībnieki pieslēdzas ar izslēgt skaņu",
|
||||
"muteEveryoneTitle": "Vai izslēgt skaņu visiem?",
|
||||
"muteEveryonesVideoDialog": "Dalībnieki var jebkurā laikā ieslēgt savu video.",
|
||||
"muteEveryonesVideoDialogModerationOn": "Dalībnieki jebkurā laikā var nosūtīt pieprasījumu ieslēgt viņu video.",
|
||||
"muteEveryonesVideoDialog": "Dalībnieki var ieslēgt savu video.",
|
||||
"muteEveryonesVideoDialogModerationOn": "Dalībnieki var nosūtīt pieprasījumu ieslēgt viņu video.",
|
||||
"muteEveryonesVideoDialogOk": "Atspējot",
|
||||
"muteEveryonesVideoTitle": "Vai apturēt ikviena video?",
|
||||
"muteParticipantBody": "Jūs nevarat viņiem ieslēgt skaņu, bet viņi paši to var izdarīt jebkurā laikā.",
|
||||
"muteParticipantBody": "Jūs nevariet viņiem ieslēgt skaņu, bet viņi paši to var izdarīt jebkurā laikā.",
|
||||
"muteParticipantButton": "Izslēgt skaņu",
|
||||
"muteParticipantsVideoBody": "Jūs nevarēsiet atkal ieslēgt kameru, taču viņi var to jebkurā laikā atkal ieslēgt.",
|
||||
"muteParticipantsVideoBodyModerationOn": "Jūs nevarēsiet atkal ieslēgt kameru, un viņi arī nevarēs.",
|
||||
"muteParticipantsVideoBody": "Jūs nevarēsiet kameru ieslēgt atpakaļ, taču viņi paši to varēs izdarīt jebkurā laikā.",
|
||||
"muteParticipantsVideoBodyModerationOn": "Ne Jūs, ne dalībnieki nevarēsiet ieslēgt kameru atpakaļ.",
|
||||
"muteParticipantsVideoButton": "Pārtraukt video",
|
||||
"muteParticipantsVideoDialog": "Vai tiešām vēlaties izslēgt šī dalībnieka kameru? Jūs nevarēsiet atkal ieslēgt kameru, taču viņi var to jebkurā laikā atkal ieslēgt.",
|
||||
"muteParticipantsVideoDialogModerationOn": "Vai tiešām vēlaties izslēgt šī dalībnieka kameru? Jūs nevarēsiet atkal ieslēgt kameru, un viņi arī nevarēs.",
|
||||
"muteParticipantsVideoDialog": "Vai tiešām vēlaties izslēgt šī dalībnieka kameru? Jūs nevarēsiet to ieslēgt atpakaļ, taču dalībnieks pats to varēs izdarīt jebkurā laikā.",
|
||||
"muteParticipantsVideoDialogModerationOn": "Vai tiešām vēlaties izslēgt šī dalībnieka kameru? Ne Jūs, ne dalībnieks nevarēsiet to ieslēgt atpakaļ.",
|
||||
"muteParticipantsVideoTitle": "Vai izslēgt šī dalībnieka video?",
|
||||
"noDropboxToken": "Nav derīga Dropbox tokena",
|
||||
"password": "Parole",
|
||||
@@ -733,8 +735,10 @@
|
||||
"connectedOneMember": "{{name}} ir pievienojies sapulcei",
|
||||
"connectedThreePlusMembers": "{{name}} un {{count}} citi ir pievienojušies sapulcei",
|
||||
"connectedTwoMembers": "{{first}} un {{second}} ir pievienojušies sapulcei",
|
||||
"dataChannelClosed": "Video kvalitāte ir traucēta",
|
||||
"dataChannelClosedDescription": "Savienojuma kanāls ir atvienots, un tādējādi video kvalitāte ir ierobežota līdz zemākajam iestatījumam.",
|
||||
"dataChannelClosed": "Video kvalitāte var būt traucēta",
|
||||
"dataChannelClosedDescription": "Savienojuma kanāls nedarbojas, tāpēc video kvalitāte var būt ierobežota līdz zemākajam iestatījumam.",
|
||||
"dataChannelClosedDescriptionWithAudio": "Savienojuma kanāls nedarbojas, tāpēc var rasties audio un video traucējumi.",
|
||||
"dataChannelClosedWithAudio": "Audio un video kvalitāte var būt traucēta",
|
||||
"disabledIframe": "Iegulšana ir paredzēta tikai demonstrācijas nolūkiem, tāpēc šis zvans tiks atvienots pēc {{timeout}} minūtēm.",
|
||||
"disabledIframeSecondary": "{{domain}} iegulšana ir paredzēta tikai demonstrācijas nolūkiem, tāpēc šis zvans tiks atvienots pēc {{timeout}} minūtēm. Lūdzu, izmantojiet <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi kā Pakalpojums</a> produkcijas iegulšanai!",
|
||||
"disconnected": "savienojums pārtraukts",
|
||||
@@ -832,7 +836,7 @@
|
||||
"breakoutRooms": "Grupu istabas",
|
||||
"invite": "Uzaicināt",
|
||||
"moreModerationActions": "Vairāk moderēšanas iespēju",
|
||||
"moreModerationControls": "Vairāk moderēšanas kontroļu",
|
||||
"moreModerationControls": "Vairāk moderēšanas iespēju",
|
||||
"moreParticipantOptions": "Vairāk dalībnieku iespēju",
|
||||
"mute": "Apklusināt",
|
||||
"muteAll": "Apklusināt visus",
|
||||
@@ -1482,6 +1486,10 @@
|
||||
"notification": {
|
||||
"demoteDescription": "{{actor}} pārveidoja par apmeklētāju, paceliet roku, lai piedalītos",
|
||||
"description": "Paceliet roku, lai piedalītos",
|
||||
"noMainParticipantsDescription": "Dalībniekam ir jāsāk sapulce. Lūdzu, pēc brīža mēģiniet vēlreiz.",
|
||||
"noMainParticipantsTitle": "Šī sapulce vēl nav sākusies.",
|
||||
"noVisitorLobby": "Jūs nevarat pievienoties, kamēr sapulcei ir iespējots vestibils.",
|
||||
"notAllowedPromotion": "Dalībniekam vispirms ir jāatļauj jūsu pieprasījums.",
|
||||
"title": "Jūs esat sapulces apmeklētājs"
|
||||
}
|
||||
},
|
||||
|
||||
@@ -822,8 +822,8 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"actions": {
|
||||
"admit": "Признать",
|
||||
"admitAll": "Признать все",
|
||||
"admit": "Разрешить",
|
||||
"admitAll": "Разрешить всем",
|
||||
"allow": "Разрешить",
|
||||
"allowVideo": "Разрешить видео",
|
||||
"askUnmute": "Попросить разрешение включить микрофон",
|
||||
@@ -837,7 +837,7 @@
|
||||
"mute": "Выключить звук",
|
||||
"muteAll": "Выключить звук у всех",
|
||||
"muteEveryoneElse": "Выключить микрофон у остальных",
|
||||
"reject": "Отказать",
|
||||
"reject": "Отклонить",
|
||||
"stopEveryonesVideo": "Выключить у всех камеру",
|
||||
"stopVideo": "Остановить видео",
|
||||
"unblockEveryoneMicCamera": "Разблокировать у всех микрофон и камеру",
|
||||
|
||||
@@ -128,6 +128,7 @@
|
||||
"privateNotice": "{{recipient}} için özel mesaj",
|
||||
"sendButton": "Gönder",
|
||||
"smileysPanel": "Emoji paneli",
|
||||
"systemDisplayName": "Sistem",
|
||||
"tabs": {
|
||||
"chat": "Sohbet",
|
||||
"polls": "Anket"
|
||||
@@ -735,6 +736,8 @@
|
||||
"connectedTwoMembers": "{{first}} ve {{second}} toplantıya katıldı",
|
||||
"dataChannelClosed": "Video kalitesi bozuldu",
|
||||
"dataChannelClosedDescription": "Köprü kanalının bağlantısı kesildi ve bu nedenle video kalitesi en düşük ayarla sınırlandı.",
|
||||
"dataChannelClosedDescriptionWithAudio": "Köprü kanalı kapalı olduğu için ses ve video kesintileri yaşanabilir.",
|
||||
"dataChannelClosedWithAudio": "Ses ve video kalitesi etkilenebilir.",
|
||||
"disabledIframe": "Yerleştirme yalnızca demo amaçlı olduğundan bu çağrının bağlantısı {{timeout}} dakika içinde kesilecek.",
|
||||
"disabledIframeSecondary": "{{domain}} alanının yerleştirilmesi yalnızca demo amaçlı olduğundan bu çağrının bağlantısı {{timeout}} dakika içinde kesilecektir. Üretim yerleştirme için lütfen <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Hizmet olarak Jitsi</a>'yi kullanın!",
|
||||
"disconnected": "bağlantı kesildi",
|
||||
|
||||
@@ -319,6 +319,7 @@
|
||||
"embedMeeting": "Embed meeting",
|
||||
"enterDisplayName": "Enter your name",
|
||||
"error": "Error",
|
||||
"errorRoomCreationRestriction": "You tried to join too quickly, please come back in a bit.",
|
||||
"gracefulShutdown": "Our service is currently down for maintenance. Please try again later.",
|
||||
"grantModeratorDialog": "Are you sure you want to grant moderator rights to {{participantName}}?",
|
||||
"grantModeratorTitle": "Grant moderator rights",
|
||||
@@ -734,8 +735,10 @@
|
||||
"connectedOneMember": "{{name}} joined the meeting",
|
||||
"connectedThreePlusMembers": "{{name}} and many others joined the meeting",
|
||||
"connectedTwoMembers": "{{first}} and {{second}} joined the meeting",
|
||||
"dataChannelClosed": "Video quality impaired",
|
||||
"dataChannelClosedDescription": "The bridge channel has been disconnected and thus video quality is limited to its lowest setting.",
|
||||
"dataChannelClosed": "Video quality may be impaired",
|
||||
"dataChannelClosedDescription": "The bridge channel is down and thus video quality may be limited to its lowest setting.",
|
||||
"dataChannelClosedDescriptionWithAudio": "The bridge channel is down and thus disruptions to audio and video may occur.",
|
||||
"dataChannelClosedWithAudio": "Audio and video quality may be impaired",
|
||||
"disabledIframe": "Embedding is only meant for demo purposes, so this call will disconnect in {{timeout}} minutes.",
|
||||
"disabledIframeSecondary": "Embedding {{domain}} is only meant for demo purposes, so this call will disconnect in {{timeout}} minutes. Please use <a href='{{jaasDomain}}' rel='noopener noreferrer' target='_blank'>Jitsi as a Service</a> for production embedding!",
|
||||
"disconnected": "disconnected",
|
||||
@@ -1483,6 +1486,10 @@
|
||||
"notification": {
|
||||
"demoteDescription": "Sent here by {{actor}}, raise your hand to participate",
|
||||
"description": "To participate raise your hand",
|
||||
"noMainParticipantsDescription": "A participant needs to start the meeting. Please try again in a bit.",
|
||||
"noMainParticipantsTitle": "This meeting hasn’t started yet.",
|
||||
"noVisitorLobby": "You cannot join while there is a lobby enabled for the meeting.",
|
||||
"notAllowedPromotion": "A participant needs to allow your request first.",
|
||||
"title": "You are a visitor in the meeting"
|
||||
}
|
||||
},
|
||||
|
||||
10
metadata/ru/full_description.txt
Normal file
10
metadata/ru/full_description.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
Jitsi Meet позволяет вам оставаться на связи со всеми вашими командами, будь то семья, друзья или коллеги. Мгновенные видеоконференции, эффективно адаптируемые к вашим нуждам.
|
||||
|
||||
* Неограниченные пользователи: нет искусственных ограничений на количество пользователей или участников конференции. Мощность сервера и пропускная способность являются единственными ограничивающими факторами.
|
||||
* Учетная запись не требуется.
|
||||
* Закрытые комнаты: управляйте доступом к вашим конференциям с помощью пароля.
|
||||
* Зашифровано по умолчанию.
|
||||
* Высокое качество: аудио и видео доставляются с чёткостью и возможностями кодеков Opus и VP8.
|
||||
* Работает в веб браузере: вашим друзьям не требуются загрузка чтобы присоединиться к разговору. Jitsi Meet работает непосредственно в их браузерах. Чтобы начать просто поделитесь своей ссылкой на конференцию с другими.
|
||||
* 100% открытый исходный код: поддерживается удивительными сообществами со всего мира. И вашими друзьями из 8x8.
|
||||
* Приглашение по красивому адресу ссылки: вы можете встретиться с легко запоминаемым https://MySite.com/OurConf по своему выбору вместо случайных цифр.
|
||||
1
metadata/ru/short_description.txt
Normal file
1
metadata/ru/short_description.txt
Normal file
@@ -0,0 +1 @@
|
||||
Защищённые, простые и масштабируемые видеоконференции с качественным видео
|
||||
@@ -41,6 +41,7 @@ import {
|
||||
import { LOCAL_PARTICIPANT_DEFAULT_ID } from '../../react/features/base/participants/constants';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getNormalizedDisplayName,
|
||||
getParticipantById,
|
||||
getScreenshareParticipantIds,
|
||||
getVirtualScreenshareParticipantByOwnerId,
|
||||
@@ -76,6 +77,7 @@ import { setMediaEncryptionKey, toggleE2EE } from '../../react/features/e2ee/act
|
||||
import {
|
||||
addStageParticipant,
|
||||
resizeFilmStrip,
|
||||
setFilmstripVisible,
|
||||
setVolume,
|
||||
togglePinStageParticipant
|
||||
} from '../../react/features/filmstrip/actions.web';
|
||||
@@ -199,7 +201,7 @@ function initCommands() {
|
||||
},
|
||||
'display-name': displayName => {
|
||||
sendAnalytics(createApiEvent('display.name.changed'));
|
||||
APP.conference.changeLocalDisplayName(displayName);
|
||||
APP.store.dispatch(updateSettings({ displayName: getNormalizedDisplayName(displayName) }));
|
||||
},
|
||||
'local-subject': localSubject => {
|
||||
sendAnalytics(createApiEvent('local.subject.changed'));
|
||||
@@ -376,7 +378,9 @@ function initCommands() {
|
||||
},
|
||||
'toggle-film-strip': () => {
|
||||
sendAnalytics(createApiEvent('film.strip.toggled'));
|
||||
APP.UI.toggleFilmstrip();
|
||||
const { visible } = APP.store.getState()['features/filmstrip'];
|
||||
|
||||
APP.store.dispatch(setFilmstripVisible(!visible));
|
||||
},
|
||||
|
||||
/*
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
const UI = {};
|
||||
|
||||
import Logger from '@jitsi/logger';
|
||||
import EventEmitter from 'events';
|
||||
|
||||
import {
|
||||
conferenceWillInit
|
||||
@@ -13,7 +12,6 @@ import { isMobileBrowser } from '../../react/features/base/environment/utils';
|
||||
import { setColorAlpha } from '../../react/features/base/util/helpers';
|
||||
import { sanitizeUrl } from '../../react/features/base/util/uri';
|
||||
import { setDocumentUrl } from '../../react/features/etherpad/actions';
|
||||
import { setFilmstripVisible } from '../../react/features/filmstrip/actions.any';
|
||||
import {
|
||||
setNotificationsEnabled,
|
||||
showNotification
|
||||
@@ -25,7 +23,6 @@ import {
|
||||
setToolboxEnabled,
|
||||
showToolbox
|
||||
} from '../../react/features/toolbox/actions.web';
|
||||
import UIEvents from '../../service/UI/UIEvents';
|
||||
|
||||
import EtherpadManager from './etherpad/Etherpad';
|
||||
import UIUtil from './util/UIUtil';
|
||||
@@ -33,22 +30,8 @@ import VideoLayout from './videolayout/VideoLayout';
|
||||
|
||||
const logger = Logger.getLogger(__filename);
|
||||
|
||||
const eventEmitter = new EventEmitter();
|
||||
|
||||
UI.eventEmitter = eventEmitter;
|
||||
|
||||
let etherpadManager;
|
||||
|
||||
const UIListeners = new Map([
|
||||
[
|
||||
UIEvents.ETHERPAD_CLICKED,
|
||||
() => etherpadManager && etherpadManager.toggleEtherpad()
|
||||
], [
|
||||
UIEvents.TOGGLE_FILMSTRIP,
|
||||
() => UI.toggleFilmstrip()
|
||||
]
|
||||
]);
|
||||
|
||||
/**
|
||||
* Indicates if we're currently in full screen mode.
|
||||
*
|
||||
@@ -96,10 +79,11 @@ UI.start = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* Setup some UI event listeners.
|
||||
* Handles etherpad click.
|
||||
*/
|
||||
UI.registerListeners
|
||||
= () => UIListeners.forEach((value, key) => UI.addListener(key, value));
|
||||
UI.onEtherpadClicked = function() {
|
||||
etherpadManager && etherpadManager.toggleEtherpad();
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -143,7 +127,7 @@ UI.initEtherpad = name => {
|
||||
}
|
||||
logger.log('Etherpad is enabled');
|
||||
|
||||
etherpadManager = new EtherpadManager(eventEmitter);
|
||||
etherpadManager = new EtherpadManager();
|
||||
|
||||
const url = new URL(name, etherpadBaseUrl);
|
||||
|
||||
@@ -197,15 +181,6 @@ UI.updateUserStatus = (user, status) => {
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.SHORT));
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggles filmstrip.
|
||||
*/
|
||||
UI.toggleFilmstrip = function() {
|
||||
const { visible } = APP.store.getState()['features/filmstrip'];
|
||||
|
||||
APP.store.dispatch(setFilmstripVisible(!visible));
|
||||
};
|
||||
|
||||
/**
|
||||
* Sets muted video state for participant
|
||||
*/
|
||||
@@ -219,33 +194,6 @@ UI.setVideoMuted = function(id) {
|
||||
|
||||
UI.updateLargeVideo = (id, forceUpdate) => VideoLayout.updateLargeVideo(id, forceUpdate);
|
||||
|
||||
/**
|
||||
* Adds a listener that would be notified on the given type of event.
|
||||
*
|
||||
* @param type the type of the event we're listening for
|
||||
* @param listener a function that would be called when notified
|
||||
*/
|
||||
UI.addListener = function(type, listener) {
|
||||
eventEmitter.on(type, listener);
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the all listeners for all events.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
UI.removeAllListeners = function() {
|
||||
eventEmitter.removeAllListeners();
|
||||
};
|
||||
|
||||
/**
|
||||
* Emits the event of given type by specifying the parameters in options.
|
||||
*
|
||||
* @param type the type of the event we're emitting
|
||||
* @param options the parameters for the event
|
||||
*/
|
||||
UI.emitEvent = (type, ...options) => eventEmitter.emit(type, ...options);
|
||||
|
||||
// Used by torture.
|
||||
UI.showToolbar = timeout => APP.store.dispatch(showToolbox(timeout));
|
||||
|
||||
|
||||
@@ -138,8 +138,7 @@ export default class EtherpadManager {
|
||||
/**
|
||||
*
|
||||
*/
|
||||
constructor(eventEmitter) {
|
||||
this.eventEmitter = eventEmitter;
|
||||
constructor() {
|
||||
this.etherpad = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { createScreenSharingIssueEvent } from '../../../react/features/analytics
|
||||
import { sendAnalytics } from '../../../react/features/analytics/functions';
|
||||
import Avatar from '../../../react/features/base/avatar/components/Avatar';
|
||||
import theme from '../../../react/features/base/components/themes/participantsPaneTheme.json';
|
||||
import { getSsrcRewritingFeatureFlag } from '../../../react/features/base/config/functions.any';
|
||||
import i18next from '../../../react/features/base/i18n/i18next';
|
||||
import { JitsiTrackEvents } from '../../../react/features/base/lib-jitsi-meet';
|
||||
import { VIDEO_TYPE } from '../../../react/features/base/media/constants';
|
||||
@@ -221,10 +222,10 @@ export default class LargeVideoManager {
|
||||
|
||||
this.updateInProcess = true;
|
||||
|
||||
// Include hide()/fadeOut only if we're switching between users
|
||||
// eslint-disable-next-line eqeqeq
|
||||
// Include hide()/fadeOut if we're switching between users or between different sources of the same user.
|
||||
const container = this.getCurrentContainer();
|
||||
const isUserSwitch = this.newStreamData.id !== container.id;
|
||||
const isUserSwitch = container.id !== this.newStreamData.id
|
||||
|| container.stream?.getSourceName() !== this.newStreamData.stream?.getSourceName();
|
||||
const preUpdate = isUserSwitch ? container.hide() : Promise.resolve();
|
||||
|
||||
preUpdate.then(() => {
|
||||
|
||||
@@ -501,8 +501,7 @@ export class VideoContainer extends LargeContainer {
|
||||
* @param {string} videoType video type
|
||||
*/
|
||||
setStream(userID, stream, videoType) {
|
||||
this.userId = userID;
|
||||
if (this.stream === stream && !stream?.forceStreamToReattach) {
|
||||
if (this.userId === userID && this.stream === stream && !stream?.forceStreamToReattach) {
|
||||
logger.debug(`SetStream on the large video for user ${userID} ignored: the stream is not changed!`);
|
||||
|
||||
// Handles the use case for the remote participants when the
|
||||
@@ -516,6 +515,8 @@ export class VideoContainer extends LargeContainer {
|
||||
return;
|
||||
}
|
||||
|
||||
this.userId = userID;
|
||||
|
||||
if (stream?.forceStreamToReattach) {
|
||||
delete stream.forceStreamToReattach;
|
||||
}
|
||||
@@ -540,9 +541,8 @@ export class VideoContainer extends LargeContainer {
|
||||
logger.error(`Attaching the remote track ${stream} to large video has failed with `, error);
|
||||
});
|
||||
|
||||
// Ensure large video gets play() called on it when a new stream is attached to it. This is necessary in the
|
||||
// case of Safari as autoplay doesn't kick-in automatically on Safari 15 and newer versions.
|
||||
browser.isWebKitBased() && this._play();
|
||||
// Ensure large video gets play() called on it when a new stream is attached to it.
|
||||
this._play();
|
||||
|
||||
const flipX = stream.isLocal() && this.localFlipX && !this.isScreenSharing();
|
||||
|
||||
|
||||
80
package-lock.json
generated
80
package-lock.json
generated
@@ -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/v1812.0.0+2eddb859/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1823.0.0+ec98b020/lib-jitsi-meet.tgz",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
@@ -101,7 +101,7 @@
|
||||
"react-native-video": "6.0.0-alpha.11",
|
||||
"react-native-watch-connectivity": "1.1.0",
|
||||
"react-native-webrtc": "118.0.7",
|
||||
"react-native-webview": "13.5.1",
|
||||
"react-native-webview": "13.8.7",
|
||||
"react-native-youtube-iframe": "2.3.0",
|
||||
"react-redux": "7.2.9",
|
||||
"react-textarea-autosize": "8.3.0",
|
||||
@@ -10875,9 +10875,9 @@
|
||||
"integrity": "sha512-nPer0rjtzdZ7csVIu233P2cUm/ks/4aVSI+5KUkYrYpgA7ujgC3p6J7FtFU+AIMWwnwYQOB/yeiOITxFeYIXiw=="
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.4",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
|
||||
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
|
||||
"version": "1.15.6",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
||||
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
@@ -10934,9 +10934,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/fs-monkey": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz",
|
||||
"integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==",
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz",
|
||||
"integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/fs.realpath": {
|
||||
@@ -12866,8 +12866,8 @@
|
||||
},
|
||||
"node_modules/lib-jitsi-meet": {
|
||||
"version": "0.0.0",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1812.0.0+2eddb859/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-svKmkJzs6BNrOgTHST7aAxpGHLAVAlxuX7VQrGVL8HUpU6FDrqHBRSMQXX0937jir6OpIhSCjoFK6maZ9xfOZg==",
|
||||
"resolved": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1823.0.0+ec98b020/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-jbROcnR1IWzp8I7quwj9uA8LMDE99tS6Wkxxf4ESAsYpc5wjbfJdqGaXKiIreWvlqM5NJTd/jMjw+DrZzbif1Q==",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -13492,12 +13492,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/memfs": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz",
|
||||
"integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==",
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz",
|
||||
"integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"fs-monkey": "1.0.3"
|
||||
"fs-monkey": "^1.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
@@ -16938,9 +16938,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-webview": {
|
||||
"version": "13.5.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.5.1.tgz",
|
||||
"integrity": "sha512-SZQJqFUMxYYj1xYWy1Z48WcHpqOGvbXKS5R1cnaLQY/JxefS+1NOVMqWxy1Zwmc128vqtRklES8l9Jus8tKSLg==",
|
||||
"version": "13.8.7",
|
||||
"resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.8.7.tgz",
|
||||
"integrity": "sha512-r8y+qlyh2+mtuPhOvgPB9Ccn2itDXacvaVFuB61AYTfisz1YhJ57iFEE8j32DfDpvl6NhaaFIzzHsumwJk92bQ==",
|
||||
"dependencies": {
|
||||
"escape-string-regexp": "2.0.0",
|
||||
"invariant": "2.2.4"
|
||||
@@ -19623,13 +19623,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/webpack-dev-middleware": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz",
|
||||
"integrity": "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==",
|
||||
"version": "5.3.4",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz",
|
||||
"integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"colorette": "^2.0.10",
|
||||
"memfs": "^3.4.1",
|
||||
"memfs": "^3.4.3",
|
||||
"mime-types": "^2.1.31",
|
||||
"range-parser": "^1.2.1",
|
||||
"schema-utils": "^4.0.0"
|
||||
@@ -27947,9 +27947,9 @@
|
||||
"integrity": "sha512-nPer0rjtzdZ7csVIu233P2cUm/ks/4aVSI+5KUkYrYpgA7ujgC3p6J7FtFU+AIMWwnwYQOB/yeiOITxFeYIXiw=="
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.15.4",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz",
|
||||
"integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==",
|
||||
"version": "1.15.6",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.6.tgz",
|
||||
"integrity": "sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==",
|
||||
"dev": true
|
||||
},
|
||||
"for-each": {
|
||||
@@ -27983,9 +27983,9 @@
|
||||
}
|
||||
},
|
||||
"fs-monkey": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.3.tgz",
|
||||
"integrity": "sha512-cybjIfiiE+pTWicSCLFHSrXZ6EilF30oh91FDP9S2B051prEa7QWfrVTQm10/dDpswBDXZugPa1Ogu8Yh+HV0Q==",
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/fs-monkey/-/fs-monkey-1.0.6.tgz",
|
||||
"integrity": "sha512-b1FMfwetIKymC0eioW7mTywihSQE4oLzQn1dB6rZB5fx/3NpNEdAWeCSMB+60/AeT0TCXsxzAlcYVEFCTAksWg==",
|
||||
"dev": true
|
||||
},
|
||||
"fs.realpath": {
|
||||
@@ -29374,8 +29374,8 @@
|
||||
}
|
||||
},
|
||||
"lib-jitsi-meet": {
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1812.0.0+2eddb859/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-svKmkJzs6BNrOgTHST7aAxpGHLAVAlxuX7VQrGVL8HUpU6FDrqHBRSMQXX0937jir6OpIhSCjoFK6maZ9xfOZg==",
|
||||
"version": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1823.0.0+ec98b020/lib-jitsi-meet.tgz",
|
||||
"integrity": "sha512-jbROcnR1IWzp8I7quwj9uA8LMDE99tS6Wkxxf4ESAsYpc5wjbfJdqGaXKiIreWvlqM5NJTd/jMjw+DrZzbif1Q==",
|
||||
"requires": {
|
||||
"@jitsi/js-utils": "2.2.1",
|
||||
"@jitsi/logger": "2.0.2",
|
||||
@@ -29867,12 +29867,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"memfs": {
|
||||
"version": "3.4.1",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.4.1.tgz",
|
||||
"integrity": "sha512-1c9VPVvW5P7I85c35zAdEr1TD5+F11IToIHIlrVIcflfnzPkJa0ZoYEoEdYDP8KgPFoSZ/opDrUsAoZWym3mtw==",
|
||||
"version": "3.5.3",
|
||||
"resolved": "https://registry.npmjs.org/memfs/-/memfs-3.5.3.tgz",
|
||||
"integrity": "sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fs-monkey": "1.0.3"
|
||||
"fs-monkey": "^1.0.4"
|
||||
}
|
||||
},
|
||||
"memoize-one": {
|
||||
@@ -32329,9 +32329,9 @@
|
||||
}
|
||||
},
|
||||
"react-native-webview": {
|
||||
"version": "13.5.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.5.1.tgz",
|
||||
"integrity": "sha512-SZQJqFUMxYYj1xYWy1Z48WcHpqOGvbXKS5R1cnaLQY/JxefS+1NOVMqWxy1Zwmc128vqtRklES8l9Jus8tKSLg==",
|
||||
"version": "13.8.7",
|
||||
"resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-13.8.7.tgz",
|
||||
"integrity": "sha512-r8y+qlyh2+mtuPhOvgPB9Ccn2itDXacvaVFuB61AYTfisz1YhJ57iFEE8j32DfDpvl6NhaaFIzzHsumwJk92bQ==",
|
||||
"requires": {
|
||||
"escape-string-regexp": "2.0.0",
|
||||
"invariant": "2.2.4"
|
||||
@@ -34260,13 +34260,13 @@
|
||||
}
|
||||
},
|
||||
"webpack-dev-middleware": {
|
||||
"version": "5.3.1",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.1.tgz",
|
||||
"integrity": "sha512-81EujCKkyles2wphtdrnPg/QqegC/AtqNH//mQkBYSMqwFVCQrxM6ktB2O/SPlZy7LqeEfTbV3cZARGQz6umhg==",
|
||||
"version": "5.3.4",
|
||||
"resolved": "https://registry.npmjs.org/webpack-dev-middleware/-/webpack-dev-middleware-5.3.4.tgz",
|
||||
"integrity": "sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"colorette": "^2.0.10",
|
||||
"memfs": "^3.4.1",
|
||||
"memfs": "^3.4.3",
|
||||
"mime-types": "^2.1.31",
|
||||
"range-parser": "^1.2.1",
|
||||
"schema-utils": "^4.0.0"
|
||||
|
||||
@@ -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/v1812.0.0+2eddb859/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1823.0.0+ec98b020/lib-jitsi-meet.tgz",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
@@ -107,7 +107,7 @@
|
||||
"react-native-video": "6.0.0-alpha.11",
|
||||
"react-native-watch-connectivity": "1.1.0",
|
||||
"react-native-webrtc": "118.0.7",
|
||||
"react-native-webview": "13.5.1",
|
||||
"react-native-webview": "13.8.7",
|
||||
"react-native-youtube-iframe": "2.3.0",
|
||||
"react-redux": "7.2.9",
|
||||
"react-textarea-autosize": "8.3.0",
|
||||
|
||||
@@ -95,7 +95,7 @@ https://github.com/jitsi/jitsi-meet-sdk-samples/tree/master/react-native
|
||||
|
||||
|
||||
## Using JWT tokens
|
||||
- If you are planning to use tokens or another domain you can do that by updating the following props, as shown below.
|
||||
- If you are planning to use tokens or another domain, you can do that by updating the following props, as shown below.
|
||||
- For example:
|
||||
```javascript
|
||||
<JitsiMeeting
|
||||
@@ -105,7 +105,7 @@ https://github.com/jitsi/jitsi-meet-sdk-samples/tree/master/react-native
|
||||
```
|
||||
|
||||
## Using custom overflow menu buttons
|
||||
- If you are planning to use tokens or another domain you can do that by updating the following props, as shown below.
|
||||
- If you are planning to add custom overflow menu buttons, you can do that by updating the ```config``` prop, as shown below.
|
||||
- For example:
|
||||
```javascript
|
||||
<JitsiMeeting
|
||||
|
||||
@@ -331,15 +331,17 @@ export function createNetworkInfoEvent({ isOnline, networkType, details }:
|
||||
/**
|
||||
* Creates a "not allowed error" event.
|
||||
*
|
||||
* @param {string} type - The type of the error.
|
||||
* @param {string} reason - The reason for the error.
|
||||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export function createNotAllowedErrorEvent(reason: string) {
|
||||
export function createNotAllowedErrorEvent(type: string, reason: string) {
|
||||
return {
|
||||
action: 'not.allowed.error',
|
||||
attributes: {
|
||||
reason
|
||||
reason,
|
||||
type
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -72,8 +72,7 @@ export const _getTokenAuthState = (
|
||||
// @ts-ignore
|
||||
state['config.startWithVideoMuted'] = true;
|
||||
}
|
||||
|
||||
const params = parseURLParams(locationURL, true);
|
||||
const params = parseURLParams(locationURL);
|
||||
|
||||
for (const key of Object.keys(params)) {
|
||||
// we allow only config and interfaceConfig overrides in the state
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @ts-expect-error
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
import { createAudioOnlyChangedEvent } from '../../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../../analytics/functions';
|
||||
import { IStore } from '../../app/types';
|
||||
@@ -33,7 +31,7 @@ export function setAudioOnly(audioOnly: boolean) {
|
||||
if (typeof APP !== 'undefined') {
|
||||
// TODO This should be a temporary solution that lasts only until video
|
||||
// tracks and all ui is moved into react/redux on the web.
|
||||
APP.UI.emitEvent(UIEvents.TOGGLE_AUDIO_ONLY, audioOnly);
|
||||
APP.conference.onToggleAudioOnly();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @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';
|
||||
@@ -1066,41 +1064,41 @@ export function redirect(vnode: string, focusJid: string, username: string) {
|
||||
// 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 (!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'];
|
||||
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));
|
||||
// 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);
|
||||
}
|
||||
// // FIXME: The old conference logic still relies on this event being emitted.
|
||||
typeof APP === 'undefined' || APP.conference.muteAudio(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.conference.muteVideo(false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: Workaround for the web version. To be removed once we get rid of conference.js
|
||||
if (typeof APP !== 'undefined') {
|
||||
APP.conference.startConference([]);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -165,6 +165,10 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
|
||||
const result = next(action);
|
||||
const { enableForcedReload } = getState()['features/base/config'];
|
||||
|
||||
if (LocalRecordingManager.isRecordingLocally()) {
|
||||
dispatch(stopLocalVideoRecording());
|
||||
}
|
||||
|
||||
// Handle specific failure reasons.
|
||||
switch (error.name) {
|
||||
case JitsiConferenceErrors.CONFERENCE_RESTARTED: {
|
||||
@@ -220,9 +224,30 @@ function _conferenceFailed({ dispatch, getState }: IStore, next: Function, actio
|
||||
break;
|
||||
}
|
||||
case JitsiConferenceErrors.NOT_ALLOWED_ERROR: {
|
||||
const [ msg ] = error.params;
|
||||
const [ type, msg ] = error.params;
|
||||
|
||||
let descriptionKey;
|
||||
let titleKey = 'dialog.tokenAuthFailed';
|
||||
|
||||
if (type === JitsiConferenceErrors.AUTH_ERROR_TYPES.NO_MAIN_PARTICIPANTS) {
|
||||
descriptionKey = 'visitors.notification.noMainParticipantsDescription';
|
||||
titleKey = 'visitors.notification.noMainParticipantsTitle';
|
||||
} else if (type === JitsiConferenceErrors.AUTH_ERROR_TYPES.NO_VISITORS_LOBBY) {
|
||||
descriptionKey = 'visitors.notification.noVisitorLobby';
|
||||
} else if (type === JitsiConferenceErrors.AUTH_ERROR_TYPES.PROMOTION_NOT_ALLOWED) {
|
||||
descriptionKey = 'visitors.notification.notAllowedPromotion';
|
||||
} else if (type === JitsiConferenceErrors.AUTH_ERROR_TYPES.ROOM_CREATION_RESTRICTION) {
|
||||
descriptionKey = 'dialog.errorRoomCreationRestriction';
|
||||
}
|
||||
|
||||
APP.store.dispatch(showErrorNotification({
|
||||
descriptionKey,
|
||||
hideErrorSupportLink: true,
|
||||
titleKey
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.STICKY));
|
||||
|
||||
sendAnalytics(createNotAllowedErrorEvent(type, msg));
|
||||
|
||||
sendAnalytics(createNotAllowedErrorEvent(msg));
|
||||
break;
|
||||
}
|
||||
case JitsiConferenceErrors.OFFER_ANSWER_FAILED:
|
||||
|
||||
@@ -18,6 +18,8 @@ import {
|
||||
CONFERENCE_TIMESTAMP_CHANGED,
|
||||
CONFERENCE_WILL_JOIN,
|
||||
CONFERENCE_WILL_LEAVE,
|
||||
DATA_CHANNEL_CLOSED,
|
||||
DATA_CHANNEL_OPENED,
|
||||
LOCK_STATE_CHANGED,
|
||||
P2P_STATUS_CHANGED,
|
||||
SET_ASSUMED_BANDWIDTH_BPS,
|
||||
@@ -35,6 +37,7 @@ import { isRoomValid } from './functions';
|
||||
const DEFAULT_STATE = {
|
||||
assumedBandwidthBps: undefined,
|
||||
conference: undefined,
|
||||
dataChannelOpen: undefined,
|
||||
e2eeSupported: undefined,
|
||||
joining: undefined,
|
||||
leaving: undefined,
|
||||
@@ -146,6 +149,7 @@ export interface IConferenceState {
|
||||
authRequired?: IJitsiConference;
|
||||
conference?: IJitsiConference;
|
||||
conferenceTimestamp?: number;
|
||||
dataChannelOpen?: boolean;
|
||||
e2eeSupported?: boolean;
|
||||
error?: Error;
|
||||
followMeEnabled?: boolean;
|
||||
@@ -219,6 +223,12 @@ ReducerRegistry.register<IConferenceState>('features/base/conference',
|
||||
case CONNECTION_WILL_CONNECT:
|
||||
return set(state, 'authRequired', undefined);
|
||||
|
||||
case DATA_CHANNEL_CLOSED:
|
||||
return set(state, 'dataChannelOpen', false);
|
||||
|
||||
case DATA_CHANNEL_OPENED:
|
||||
return set(state, 'dataChannelOpen', true);
|
||||
|
||||
case LOCK_STATE_CHANGED:
|
||||
return _lockStateChanged(state, action);
|
||||
|
||||
|
||||
@@ -81,6 +81,7 @@ export default [
|
||||
'debug',
|
||||
'debugAudioLevels',
|
||||
'deeplinking.disabled',
|
||||
'deeplinking.desktop.enabled',
|
||||
'defaultLocalDisplayName',
|
||||
'defaultRemoteDisplayName',
|
||||
'deploymentUrls',
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { AnyAction } from 'redux';
|
||||
|
||||
// @ts-expect-error
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
import { IStore } from '../../app/types';
|
||||
import { processExternalDeviceRequest } from '../../device-selection/functions';
|
||||
import { showNotification, showWarningNotification } from '../../notifications/actions';
|
||||
@@ -161,7 +159,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
if (isPrejoinPageVisible(store.getState())) {
|
||||
store.dispatch(replaceAudioTrackById(action.deviceId));
|
||||
} else {
|
||||
APP.UI.emitEvent(UIEvents.AUDIO_DEVICE_CHANGED, action.deviceId);
|
||||
APP.conference.onAudioDeviceChanged(action.deviceId);
|
||||
}
|
||||
break;
|
||||
case SET_VIDEO_INPUT_DEVICE: {
|
||||
@@ -174,7 +172,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
if (isPrejoinPageVisible(store.getState())) {
|
||||
store.dispatch(replaceVideoTrackById(action.deviceId));
|
||||
} else {
|
||||
APP.UI.emitEvent(UIEvents.VIDEO_DEVICE_CHANGED, action.deviceId);
|
||||
APP.conference.onVideoDeviceChanged(action.deviceId);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -43,18 +43,6 @@ export const KICK_PARTICIPANT = 'KICK_PARTICIPANT';
|
||||
*/
|
||||
export const MUTE_REMOTE_PARTICIPANT = 'MUTE_REMOTE_PARTICIPANT';
|
||||
|
||||
/**
|
||||
* Create an action for when the local participant's display name is updated.
|
||||
*
|
||||
* {
|
||||
* type: PARTICIPANT_DISPLAY_NAME_CHANGED,
|
||||
* id: string,
|
||||
* name: string
|
||||
* }
|
||||
*/
|
||||
export const PARTICIPANT_DISPLAY_NAME_CHANGED
|
||||
= 'PARTICIPANT_DISPLAY_NAME_CHANGED';
|
||||
|
||||
/**
|
||||
* Action to signal that ID of participant has changed. This happens when
|
||||
* local participant joins a new conference or quits one.
|
||||
|
||||
@@ -12,9 +12,9 @@ import { getCurrentConference } from '../conference/functions';
|
||||
import { ADD_PEOPLE_ENABLED } from '../flags/constants';
|
||||
import { getFeatureFlag } from '../flags/functions';
|
||||
import i18next from '../i18n/i18next';
|
||||
import { MEDIA_TYPE, VIDEO_TYPE } from '../media/constants';
|
||||
import { MEDIA_TYPE, MediaType, VIDEO_TYPE } from '../media/constants';
|
||||
import { toState } from '../redux/functions';
|
||||
import { getScreenShareTrack } from '../tracks/functions.any';
|
||||
import { getScreenShareTrack, isLocalTrackMuted } from '../tracks/functions.any';
|
||||
import { createDeferred } from '../util/helpers';
|
||||
|
||||
import {
|
||||
@@ -362,6 +362,42 @@ export function getRemoteParticipantCountWithFake(stateful: IStateful) {
|
||||
return participantsState.remote.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the muted state of the given media source for a given participant.
|
||||
*
|
||||
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's.
|
||||
* @param {IParticipant} participant - The participant entity.
|
||||
* @param {MediaType} mediaType - The media type.
|
||||
* @returns {boolean} - True its muted, false otherwise.
|
||||
*/
|
||||
export function getMutedStateByParticipantAndMediaType(
|
||||
stateful: IStateful,
|
||||
participant: IParticipant,
|
||||
mediaType: MediaType): boolean {
|
||||
const type = mediaType === MEDIA_TYPE.SCREENSHARE ? 'video' : mediaType;
|
||||
|
||||
if (participant.local) {
|
||||
const state = toState(stateful);
|
||||
const tracks = state['features/base/tracks'];
|
||||
|
||||
return isLocalTrackMuted(tracks, mediaType);
|
||||
}
|
||||
|
||||
const sources = participant.sources?.get(type);
|
||||
|
||||
if (!sources) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (mediaType === MEDIA_TYPE.AUDIO) {
|
||||
return Array.from(sources.values())[0].muted;
|
||||
}
|
||||
const videoType = mediaType === MEDIA_TYPE.VIDEO ? VIDEO_TYPE.CAMERA : VIDEO_TYPE.SCREENSHARE;
|
||||
const source = Array.from(sources.values()).find(src => src.videoType === videoType);
|
||||
|
||||
return source?.muted ?? true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a count of the known participants in the passed in redux state,
|
||||
* including fake participants.
|
||||
@@ -468,28 +504,28 @@ export function getScreenshareParticipantIds(stateful: IStateful): Array<string>
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list source name associated with a given remote participant and for the given media type.
|
||||
* Returns a list of source names associated with a given remote participant and for the given media type.
|
||||
*
|
||||
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's {@code getState} function to be used to
|
||||
* retrieve the state.
|
||||
* @param {string} id - The id of the participant whose source names are to be retrieved.
|
||||
* @param {string} mediaType - The type of source, audio or video.
|
||||
* @returns {Array<string>|undefined}
|
||||
* @returns {Array<string>}
|
||||
*/
|
||||
export function getSourceNamesByMediaType(
|
||||
export function getSourceNamesByMediaTypeAndParticipant(
|
||||
stateful: IStateful,
|
||||
id: string,
|
||||
mediaType: string): Array<string> | undefined {
|
||||
mediaType: string): Array<string> {
|
||||
const participant: IParticipant | undefined = getParticipantById(stateful, id);
|
||||
|
||||
if (!participant) {
|
||||
return;
|
||||
return [];
|
||||
}
|
||||
|
||||
const sources = participant.sources;
|
||||
|
||||
if (!sources) {
|
||||
return;
|
||||
return [];
|
||||
}
|
||||
|
||||
return Array.from(sources.get(mediaType) ?? new Map())
|
||||
@@ -497,6 +533,37 @@ export function getSourceNamesByMediaType(
|
||||
.map(s => s[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of source names associated with a given remote participant and for the given video type (only for
|
||||
* video sources).
|
||||
*
|
||||
* @param {(Function|Object)} stateful - The (whole) redux state, or redux's {@code getState} function to be used to
|
||||
* retrieve the state.
|
||||
* @param {string} id - The id of the participant whose source names are to be retrieved.
|
||||
* @param {string} videoType - The type of video, camera or desktop.
|
||||
* @returns {Array<string>}
|
||||
*/
|
||||
export function getSourceNamesByVideoTypeAndParticipant(
|
||||
stateful: IStateful,
|
||||
id: string,
|
||||
videoType: string): Array<string> {
|
||||
const participant: IParticipant | undefined = getParticipantById(stateful, id);
|
||||
|
||||
if (!participant) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const sources = participant.sources;
|
||||
|
||||
if (!sources) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Array.from(sources.get(MEDIA_TYPE.VIDEO) ?? new Map())
|
||||
.filter(source => source[1].videoType === videoType && (videoType === VIDEO_TYPE.CAMERA || !source[1].muted))
|
||||
.map(s => s[0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the presence status of a participant associated with the passed id.
|
||||
*
|
||||
|
||||
@@ -2,8 +2,6 @@ import i18n from 'i18next';
|
||||
import { batch } from 'react-redux';
|
||||
import { AnyAction } from 'redux';
|
||||
|
||||
// @ts-expect-error
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
import { IStore } from '../../app/types';
|
||||
import { approveParticipant } from '../../av-moderation/actions';
|
||||
import { UPDATE_BREAKOUT_ROOMS } from '../../breakout-rooms/actionTypes';
|
||||
@@ -41,7 +39,6 @@ import {
|
||||
MUTE_REMOTE_PARTICIPANT,
|
||||
OVERWRITE_PARTICIPANTS_NAMES,
|
||||
OVERWRITE_PARTICIPANT_NAME,
|
||||
PARTICIPANT_DISPLAY_NAME_CHANGED,
|
||||
PARTICIPANT_JOINED,
|
||||
PARTICIPANT_LEFT,
|
||||
PARTICIPANT_UPDATED,
|
||||
@@ -234,20 +231,6 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO Remove this middleware when the local display name update flow is
|
||||
// fully brought into redux.
|
||||
case PARTICIPANT_DISPLAY_NAME_CHANGED: {
|
||||
if (typeof APP !== 'undefined') {
|
||||
const participant = getLocalParticipant(store.getState());
|
||||
|
||||
if (participant && participant.id === action.id) {
|
||||
APP.UI.emitEvent(UIEvents.NICKNAME_CHANGED, action.name);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case RAISE_HAND_UPDATED: {
|
||||
const { participant } = action;
|
||||
let queue = getRaiseHandsQueue(store.getState());
|
||||
|
||||
@@ -66,16 +66,6 @@ export const TRACK_MUTE_UNMUTE_FAILED = 'TRACK_MUTE_UNMUTE_FAILED';
|
||||
*/
|
||||
export const TRACK_NO_DATA_FROM_SOURCE = 'TRACK_NO_DATA_FROM_SOURCE';
|
||||
|
||||
/**
|
||||
* The type of redux action dispatched when the owner of a track changes due to ssrc remapping.
|
||||
*
|
||||
* {
|
||||
* type: TRACK_OWNER_CHANGED,
|
||||
* track: Track
|
||||
* }
|
||||
*/
|
||||
export const TRACK_OWNER_CHANGED = 'TRACK_OWNER_CHANGED';
|
||||
|
||||
/**
|
||||
* The type of redux action dispatched when a track has been (locally or
|
||||
* remotely) removed from the conference.
|
||||
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
TRACK_CREATE_ERROR,
|
||||
TRACK_MUTE_UNMUTE_FAILED,
|
||||
TRACK_NO_DATA_FROM_SOURCE,
|
||||
TRACK_OWNER_CHANGED,
|
||||
TRACK_REMOVED,
|
||||
TRACK_STOPPED,
|
||||
TRACK_UPDATED,
|
||||
@@ -381,9 +380,6 @@ export function trackAdded(track: any) {
|
||||
track.on(
|
||||
JitsiTrackEvents.TRACK_VIDEOTYPE_CHANGED,
|
||||
(type: VideoType) => dispatch(trackVideoTypeChanged(track, type)));
|
||||
track.on(
|
||||
JitsiTrackEvents.TRACK_OWNER_CHANGED,
|
||||
(owner: string) => dispatch(trackOwnerChanged(track, owner)));
|
||||
const local = track.isLocal();
|
||||
const mediaType = track.getVideoType() === VIDEO_TYPE.DESKTOP
|
||||
? MEDIA_TYPE.SCREENSHARE
|
||||
@@ -625,32 +621,6 @@ export function trackStreamingStatusChanged(track: any, streamingStatus: string)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an action for when the owner of the track changes due to ssrc remapping.
|
||||
*
|
||||
* @param {(JitsiRemoteTrack)} track - JitsiTrack instance.
|
||||
* @param {string} participantId - New owner's participant ID.
|
||||
* @returns {{
|
||||
* type: TRACK_OWNER_CHANGED,
|
||||
* track: Track
|
||||
* }}
|
||||
*/
|
||||
export function trackOwnerChanged(track: any, participantId: string): {
|
||||
track: {
|
||||
jitsiTrack: any;
|
||||
participantId: string;
|
||||
};
|
||||
type: 'TRACK_OWNER_CHANGED';
|
||||
} {
|
||||
return {
|
||||
type: TRACK_OWNER_CHANGED,
|
||||
track: {
|
||||
jitsiTrack: track,
|
||||
participantId
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals passed tracks to be added.
|
||||
*
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
TRACK_ADDED,
|
||||
TRACK_MUTE_UNMUTE_FAILED,
|
||||
TRACK_NO_DATA_FROM_SOURCE,
|
||||
TRACK_OWNER_CHANGED,
|
||||
TRACK_REMOVED,
|
||||
TRACK_STOPPED,
|
||||
TRACK_UPDATED
|
||||
@@ -82,23 +81,6 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
return result;
|
||||
}
|
||||
|
||||
case TRACK_OWNER_CHANGED: {
|
||||
const oldTrack = getTrackByJitsiTrack(store.getState()['features/base/tracks'], action.track?.jitsiTrack);
|
||||
const oldOwner = oldTrack?.participantId;
|
||||
const result = next(action);
|
||||
const newOwner = action.track?.participantId;
|
||||
|
||||
if (oldOwner) {
|
||||
logTracksForParticipant(store.getState()['features/base/tracks'], oldOwner, 'Owner changed');
|
||||
}
|
||||
|
||||
if (newOwner) {
|
||||
logTracksForParticipant(store.getState()['features/base/tracks'], newOwner, 'Owner changed');
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
case TRACK_MUTE_UNMUTE_FAILED: {
|
||||
const { jitsiTrack } = action.track;
|
||||
const muted = action.wasMuted;
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
TRACK_CREATE_CANCELED,
|
||||
TRACK_CREATE_ERROR,
|
||||
TRACK_NO_DATA_FROM_SOURCE,
|
||||
TRACK_OWNER_CHANGED,
|
||||
TRACK_REMOVED,
|
||||
TRACK_UPDATED,
|
||||
TRACK_WILL_CREATE
|
||||
@@ -43,18 +42,6 @@ function track(state: ITrack, action: AnyAction) {
|
||||
}
|
||||
break;
|
||||
|
||||
case TRACK_OWNER_CHANGED: {
|
||||
const t = action.track;
|
||||
|
||||
if (state.jitsiTrack === t.jitsiTrack) {
|
||||
return {
|
||||
...state,
|
||||
participantId: t.participantId
|
||||
};
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case TRACK_UPDATED: {
|
||||
const t = action.track;
|
||||
|
||||
@@ -104,7 +91,6 @@ ReducerRegistry.register<ITracksState>('features/base/tracks', (state = [], acti
|
||||
switch (action.type) {
|
||||
case PARTICIPANT_ID_CHANGED:
|
||||
case TRACK_NO_DATA_FROM_SOURCE:
|
||||
case TRACK_OWNER_CHANGED:
|
||||
case TRACK_UPDATED:
|
||||
return state.map((t: ITrack) => track(t, action));
|
||||
case TRACK_ADDED: {
|
||||
|
||||
@@ -368,8 +368,6 @@ class Conference extends AbstractConference<IProps, any> {
|
||||
*/
|
||||
_start() {
|
||||
APP.UI.start();
|
||||
|
||||
APP.UI.registerListeners();
|
||||
APP.UI.bindEvents();
|
||||
|
||||
FULL_SCREEN_EVENTS.forEach(name =>
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @ts-expect-error
|
||||
import UIEvents from '../../../service/UI/UIEvents';
|
||||
import { CONFERENCE_JOIN_IN_PROGRESS } from '../base/conference/actionTypes';
|
||||
import { getCurrentConference } from '../base/conference/functions';
|
||||
import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
|
||||
@@ -41,7 +39,7 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
||||
}
|
||||
case TOGGLE_DOCUMENT_EDITING: {
|
||||
if (typeof APP !== 'undefined') {
|
||||
APP.UI.emitEvent(UIEvents.ETHERPAD_CLICKED);
|
||||
APP.UI.onEtherpadClicked();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,12 @@ import React, { PureComponent } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState, IStore } from '../../../app/types';
|
||||
import { getSsrcRewritingFeatureFlag } from '../../../base/config/functions.any';
|
||||
import { translate } from '../../../base/i18n/functions';
|
||||
import { MEDIA_TYPE } from '../../../base/media/constants';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getMutedStateByParticipantAndMediaType,
|
||||
getParticipantById,
|
||||
getParticipantDisplayName,
|
||||
hasRaisedHand,
|
||||
@@ -166,8 +169,12 @@ function mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
const { participant } = ownProps;
|
||||
const { ownerId } = state['features/shared-video'];
|
||||
const localParticipantId = getLocalParticipant(state)?.id;
|
||||
const _isAudioMuted = Boolean(participant && isParticipantAudioMuted(participant, state));
|
||||
const _isVideoMuted = isParticipantVideoMuted(participant, state);
|
||||
const _isAudioMuted = getSsrcRewritingFeatureFlag(state)
|
||||
? Boolean(participant && getMutedStateByParticipantAndMediaType(state, participant, MEDIA_TYPE.AUDIO))
|
||||
: Boolean(participant && isParticipantAudioMuted(participant, state));
|
||||
const _isVideoMuted = getSsrcRewritingFeatureFlag(state)
|
||||
? Boolean(participant && getMutedStateByParticipantAndMediaType(state, participant, MEDIA_TYPE.VIDEO))
|
||||
: isParticipantVideoMuted(participant, state);
|
||||
const audioMediaState = getParticipantAudioMediaState(participant, _isAudioMuted, state);
|
||||
const videoMediaState = getParticipantVideoMediaState(participant, _isVideoMuted, state);
|
||||
const { disableModeratorIndicator } = state['features/base/config'];
|
||||
|
||||
@@ -2,10 +2,12 @@ import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { IReduxState } from '../../../app/types';
|
||||
import { getSsrcRewritingFeatureFlag } from '../../../base/config/functions.any';
|
||||
import { JitsiTrackEvents } from '../../../base/lib-jitsi-meet';
|
||||
import { MEDIA_TYPE } from '../../../base/media/constants';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getMutedStateByParticipantAndMediaType,
|
||||
getParticipantByIdOrUndefined,
|
||||
getParticipantDisplayName,
|
||||
hasRaisedHand,
|
||||
@@ -299,15 +301,15 @@ function _mapStateToProps(state: IReduxState, ownProps: any) {
|
||||
const { participantID, searchString } = ownProps;
|
||||
const { ownerId } = state['features/shared-video'];
|
||||
const localParticipantId = getLocalParticipant(state)?.id;
|
||||
|
||||
const participant = getParticipantByIdOrUndefined(state, participantID);
|
||||
|
||||
const _displayName = getParticipantDisplayName(state, participant?.id ?? '');
|
||||
|
||||
const _matchesSearch = participantMatchesSearch(participant, searchString);
|
||||
|
||||
const _isAudioMuted = Boolean(participant && isParticipantAudioMuted(participant, state));
|
||||
const _isVideoMuted = isParticipantVideoMuted(participant, state);
|
||||
const _isAudioMuted = getSsrcRewritingFeatureFlag(state)
|
||||
? Boolean(participant && getMutedStateByParticipantAndMediaType(state, participant, MEDIA_TYPE.AUDIO))
|
||||
: Boolean(participant && isParticipantAudioMuted(participant, state));
|
||||
const _isVideoMuted = getSsrcRewritingFeatureFlag(state)
|
||||
? Boolean(participant && getMutedStateByParticipantAndMediaType(state, participant, MEDIA_TYPE.VIDEO))
|
||||
: isParticipantVideoMuted(participant, state);
|
||||
const _audioMediaState = getParticipantAudioMediaState(participant, _isAudioMuted, state);
|
||||
const _videoMediaState = getParticipantVideoMediaState(participant, _isVideoMuted, state);
|
||||
const _quickActionButtonType = getQuickActionButtonType(participant, _isAudioMuted, _isVideoMuted, state);
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
close as closeParticipantsPane,
|
||||
open as openParticipantsPane
|
||||
} from '../../../participants-pane/actions.web';
|
||||
import { closeOverflowMenuIfOpen } from '../../../toolbox/actions.web';
|
||||
import { isParticipantsPaneEnabled } from '../../functions';
|
||||
|
||||
import ParticipantsCounter from './ParticipantsCounter';
|
||||
@@ -61,6 +62,7 @@ class ParticipantsPaneButton extends AbstractButton<IProps> {
|
||||
_handleClick() {
|
||||
const { dispatch, _isOpen } = this.props;
|
||||
|
||||
dispatch(closeOverflowMenuIfOpen());
|
||||
if (_isOpen) {
|
||||
dispatch(closeParticipantsPane());
|
||||
} else {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
import { AnyAction } from 'redux';
|
||||
|
||||
// @ts-expect-error
|
||||
import UIEvents from '../../../service/UI/UIEvents';
|
||||
import { IStore } from '../app/types';
|
||||
import {
|
||||
CONFERENCE_FAILED,
|
||||
@@ -36,12 +34,6 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
return _conferenceJoined(store, next, action);
|
||||
|
||||
case LOCK_STATE_CHANGED: {
|
||||
// TODO Remove this logic when all components interested in the lock
|
||||
// state change event are moved into react/redux.
|
||||
if (typeof APP !== 'undefined') {
|
||||
APP.UI.emitEvent(UIEvents.TOGGLE_ROOM_LOCK, action.locked);
|
||||
}
|
||||
|
||||
const previousLockedState = store.getState()['features/base/conference'].locked;
|
||||
|
||||
const result = next(action);
|
||||
|
||||
@@ -12,6 +12,7 @@ import { hangup } from '../base/connection/actions.web';
|
||||
import { openDialog } from '../base/dialog/actions';
|
||||
import i18next from '../base/i18n/i18next';
|
||||
import { browser } from '../base/lib-jitsi-meet';
|
||||
import { getNormalizedDisplayName } from '../base/participants/functions';
|
||||
import { updateSettings } from '../base/settings/actions';
|
||||
import { getLocalVideoTrack } from '../base/tracks/functions.web';
|
||||
import { appendURLHashParam } from '../base/util/uri';
|
||||
@@ -191,7 +192,7 @@ export function submitProfileTab(newState: any) {
|
||||
const currentState = getProfileTabProps(getState());
|
||||
|
||||
if (newState.displayName !== currentState.displayName) {
|
||||
APP.conference.changeLocalDisplayName(newState.displayName);
|
||||
dispatch(updateSettings({ displayName: getNormalizedDisplayName(newState.displayName) }));
|
||||
}
|
||||
|
||||
if (newState.email !== currentState.email) {
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @ts-expect-error
|
||||
import UIEvents from '../../../service/UI/UIEvents';
|
||||
import { VIDEO_MUTE, createToolbarEvent } from '../analytics/AnalyticsEvents';
|
||||
import { sendAnalytics } from '../analytics/functions';
|
||||
import { IStore } from '../app/types';
|
||||
@@ -103,7 +101,7 @@ export function handleToggleVideoMuted(muted: boolean, showUI: boolean, ensureTr
|
||||
// FIXME: The old conference logic still relies on this event being
|
||||
// emitted.
|
||||
typeof APP === 'undefined'
|
||||
|| APP.UI.emitEvent(UIEvents.VIDEO_MUTED, muted, showUI);
|
||||
|| APP.conference.muteVideo(muted, showUI);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @ts-expect-error
|
||||
import UIEvents from '../../../service/UI/UIEvents';
|
||||
import {
|
||||
AUDIO_MUTE,
|
||||
VIDEO_MUTE,
|
||||
@@ -55,8 +53,9 @@ export function muteLocal(enable: boolean, mediaType: MediaType, stopScreenShari
|
||||
: setVideoMuted(enable, VIDEO_MUTISM_AUTHORITY.USER, /* ensureTrack */ true));
|
||||
|
||||
// FIXME: The old conference logic still relies on this event being emitted.
|
||||
typeof APP === 'undefined'
|
||||
|| APP.UI.emitEvent(isAudio ? UIEvents.AUDIO_MUTED : UIEvents.VIDEO_MUTED, enable);
|
||||
if (typeof APP !== 'undefined') {
|
||||
isAudio ? APP.conference.muteAudio(enable) : APP.conference.muteVideo(enable, false);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -3,10 +3,11 @@ import debounce from 'lodash/debounce';
|
||||
import { IReduxState, IStore } from '../app/types';
|
||||
import { _handleParticipantError } from '../base/conference/functions';
|
||||
import { getSsrcRewritingFeatureFlag } from '../base/config/functions.any';
|
||||
import { MEDIA_TYPE } from '../base/media/constants';
|
||||
import { MEDIA_TYPE, VIDEO_TYPE } from '../base/media/constants';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getSourceNamesByMediaType
|
||||
getSourceNamesByMediaTypeAndParticipant,
|
||||
getSourceNamesByVideoTypeAndParticipant
|
||||
} from '../base/participants/functions';
|
||||
import StateListenerRegistry from '../base/redux/StateListenerRegistry';
|
||||
import { getTrackSourceNameByMediaTypeAndParticipant } from '../base/tracks/functions';
|
||||
@@ -321,10 +322,10 @@ function _getSourceNames(participantList: Array<string>, state: IReduxState): Ar
|
||||
|
||||
participantList.forEach(participantId => {
|
||||
if (getSsrcRewritingFeatureFlag(state)) {
|
||||
const sourceNames: string[] | undefined
|
||||
= getSourceNamesByMediaType(state, participantId, MEDIA_TYPE.VIDEO);
|
||||
const sourceNames: string[]
|
||||
= getSourceNamesByMediaTypeAndParticipant(state, participantId, MEDIA_TYPE.VIDEO);
|
||||
|
||||
sourceNames?.length && sourceNamesList.push(...sourceNames);
|
||||
sourceNames.length && sourceNamesList.push(...sourceNames);
|
||||
} else {
|
||||
let sourceName: string;
|
||||
|
||||
@@ -428,8 +429,9 @@ function _updateReceiverVideoConstraints({ getState }: IStore) {
|
||||
if (remoteScreenShares.includes(largeVideoParticipantId)) {
|
||||
largeVideoSourceName = largeVideoParticipantId;
|
||||
} else {
|
||||
largeVideoSourceName = getTrackSourceNameByMediaTypeAndParticipant(
|
||||
tracks, MEDIA_TYPE.VIDEO, largeVideoParticipantId);
|
||||
largeVideoSourceName = getSsrcRewritingFeatureFlag(state)
|
||||
? getSourceNamesByVideoTypeAndParticipant(state, largeVideoParticipantId, VIDEO_TYPE.CAMERA)[0]
|
||||
: getTrackSourceNameByMediaTypeAndParticipant(tracks, MEDIA_TYPE.VIDEO, largeVideoParticipantId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,8 @@ module:hook('muc-occupant-pre-join', function (event)
|
||||
|
||||
if host == local_domain then
|
||||
if room._main_room_lobby_enabled then
|
||||
origin.send(st.error_reply(stanza, 'cancel', 'not-allowed', 'Visitors not allowed while lobby is on!'));
|
||||
origin.send(st.error_reply(stanza, 'cancel', 'not-allowed', 'Visitors not allowed while lobby is on!')
|
||||
:tag('no-visitors-lobby', { xmlns = 'jitsi:visitors' }));
|
||||
return true;
|
||||
else
|
||||
occupant.role = 'visitor';
|
||||
|
||||
@@ -65,24 +65,27 @@ module:hook("muc-occupant-pre-join", function(event)
|
||||
room:set_affiliation(true, jid_bare(occupant_jid), "member")
|
||||
room:save_occupant(occupant);
|
||||
end
|
||||
-- bypass password on the flip device
|
||||
local join = stanza:get_child("x", MUC_NS);
|
||||
if not join then
|
||||
join = stanza:tag("x", { xmlns = MUC_NS });
|
||||
end
|
||||
local password = join:get_child("password", MUC_NS);
|
||||
if password then
|
||||
join:maptags(
|
||||
function(tag)
|
||||
for k, v in pairs(tag) do
|
||||
if k == "name" and v == "password" then
|
||||
return nil
|
||||
|
||||
if room:get_password() then
|
||||
-- bypass password on the flip device
|
||||
local join = stanza:get_child("x", MUC_NS);
|
||||
if not join then
|
||||
join = stanza:tag("x", { xmlns = MUC_NS });
|
||||
end
|
||||
local password = join:get_child("password", MUC_NS);
|
||||
if password then
|
||||
join:maptags(
|
||||
function(tag)
|
||||
for k, v in pairs(tag) do
|
||||
if k == "name" and v == "password" then
|
||||
return nil
|
||||
end
|
||||
end
|
||||
end
|
||||
return tag
|
||||
end);
|
||||
return tag
|
||||
end);
|
||||
end
|
||||
join:tag("password", { xmlns = MUC_NS }):text(room:get_password());
|
||||
end
|
||||
join:tag("password", { xmlns = MUC_NS }):text(room:get_password());
|
||||
elseif not session.jitsi_meet_context_features.flip or session.jitsi_meet_context_features.flip == false or session.jitsi_meet_context_features.flip == "false" then
|
||||
module:log("warn", "Flip device tag present without jwt permission")
|
||||
--remove flip_device tag if somebody wants to abuse this feature
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
-- Module which can be used as an http endpoint to send system chat messages to meeting participants. The provided token
|
||||
-- Module which can be used as an http endpoint to send system private chat messages to meeting participants. The provided token
|
||||
--- in the request is verified whether it has the right to do so. This module should be loaded under the virtual host.
|
||||
-- Copyright (C) 2024-present 8x8, Inc.
|
||||
|
||||
-- curl https://{host}/send-system-message -d '{"message": "testmessage", "to": "{connection_jid}", "room": "{room_jid}"}' -H "content-type: application/json" -H "authorization: Bearer {token}"
|
||||
-- curl https://{host}/send-system-chat-message -d '{"message": "testmessage", "connectionJIDs": ["{connection_jid}"], "room": "{room_jid}"}' -H "content-type: application/json" -H "authorization: Bearer {token}"
|
||||
|
||||
local util = module:require "util";
|
||||
local token_util = module:require "token/util".new(module);
|
||||
@@ -60,11 +60,11 @@ function handle_send_system_message (event)
|
||||
|
||||
local displayName = payload["displayName"];
|
||||
local message = payload["message"];
|
||||
local to = payload["to"];
|
||||
local connectionJIDs = payload["connectionJIDs"];
|
||||
local payload_room = payload["room"];
|
||||
|
||||
if not message or not to or not payload_room then
|
||||
module:log("error", "One of [message, to, room] was not provided");
|
||||
if not message or not connectionJIDs or not payload_room then
|
||||
module:log("error", "One of [message, connectionJIDs, room] was not provided");
|
||||
return { status_code = 400; }
|
||||
end
|
||||
|
||||
@@ -99,15 +99,17 @@ function handle_send_system_message (event)
|
||||
message = message,
|
||||
};
|
||||
|
||||
local stanza = st.message({
|
||||
from = room.jid,
|
||||
to = to
|
||||
})
|
||||
:tag('json-message', { xmlns = 'http://jitsi.org/jitmeet' })
|
||||
:text(json.encode(data))
|
||||
:up();
|
||||
for _, to in ipairs(connectionJIDs) do
|
||||
local stanza = st.message({
|
||||
from = room.jid,
|
||||
to = to
|
||||
})
|
||||
:tag('json-message', { xmlns = 'http://jitsi.org/jitmeet' })
|
||||
:text(json.encode(data))
|
||||
:up();
|
||||
|
||||
room:route_stanza(stanza);
|
||||
room:route_stanza(stanza);
|
||||
end
|
||||
|
||||
return { status_code = 200 };
|
||||
end
|
||||
|
||||
@@ -311,7 +311,7 @@ process_host_module(muc_domain_prefix..'.'..muc_domain_base, function(host_modul
|
||||
-- if visitor mode is started, then you are not allowed to join without request/response exchange of iqs -> deny access
|
||||
-- check list of allowed jids for the room
|
||||
host_module:hook('muc-occupant-pre-join', function (event)
|
||||
local room, stanza, occupant, origin = event.room, event.stanza, event.occupant, event.origin;
|
||||
local room, stanza, occupant, session = event.room, event.stanza, event.occupant, event.origin;
|
||||
|
||||
if is_healthcheck_room(room.jid) or is_admin(occupant.bare_jid) then
|
||||
return;
|
||||
@@ -341,8 +341,18 @@ process_host_module(muc_domain_prefix..'.'..muc_domain_base, function(host_modul
|
||||
return;
|
||||
end
|
||||
module:log('error', 'Visitor needs to be allowed by a moderator %s', stanza.attr.from);
|
||||
origin.send(st.error_reply(stanza, 'cancel', 'not-allowed', 'Visitor needs to be allowed by a moderator'));
|
||||
session.send(st.error_reply(stanza, 'cancel', 'not-allowed', 'Visitor needs to be allowed by a moderator')
|
||||
:tag('promotion-not-allowed', { xmlns = 'jitsi:visitors' }));
|
||||
return true;
|
||||
elseif is_vpaas(room) then
|
||||
-- special case for vpaas where if someone with a visitor token tries to join a room, where
|
||||
-- there are no visitors yet, we deny access
|
||||
if session.jitsi_meet_context_user and session.jitsi_meet_context_user.role == 'visitor' then
|
||||
session.log('warn', 'Deny user join as visitor in the main meeting, not approved');
|
||||
session.send(st.error_reply(
|
||||
stanza, 'cancel', 'not-allowed', 'Visitor tried to join the main room without approval')
|
||||
:tag('no-main-participants', { xmlns = 'jitsi:visitors' }));
|
||||
end
|
||||
end
|
||||
|
||||
end, 7); -- after muc_meeting_id, the logic for not joining before jicofo
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
export default {
|
||||
NICKNAME_CHANGED: 'UI.nickname_changed',
|
||||
|
||||
/**
|
||||
* Notifies that local user changed email.
|
||||
*/
|
||||
EMAIL_CHANGED: 'UI.email_changed',
|
||||
|
||||
/**
|
||||
* Notifies that "start muted" settings changed.
|
||||
*/
|
||||
AUDIO_MUTED: 'UI.audio_muted',
|
||||
VIDEO_MUTED: 'UI.video_muted',
|
||||
ETHERPAD_CLICKED: 'UI.etherpad_clicked',
|
||||
|
||||
/**
|
||||
* Updates shared video with params: url, state, time(optional)
|
||||
* Where url is the video link, state is stop/start/pause and time is the
|
||||
* current video playing time.
|
||||
*/
|
||||
TOGGLE_FULLSCREEN: 'UI.toogle_fullscreen',
|
||||
FULLSCREEN_TOGGLED: 'UI.fullscreen_toggled',
|
||||
|
||||
/**
|
||||
* Notifies that the audio only mode was toggled.
|
||||
*/
|
||||
TOGGLE_AUDIO_ONLY: 'UI.toggle_audioonly',
|
||||
|
||||
/**
|
||||
* Notifies that a command to toggle the filmstrip has been issued. The
|
||||
* event may optionally specify a {Boolean} (primitive) value to assign to
|
||||
* the visibility of the filmstrip (i.e. the event may act as a setter).
|
||||
* The very toggling of the filmstrip may or may not occurred at the time
|
||||
* of the receipt of the event depending on the position of the receiving
|
||||
* event listener in relation to the event listener which carries out the
|
||||
* command to toggle the filmstrip.
|
||||
*
|
||||
* @see {TOGGLED_FILMSTRIP}
|
||||
*/
|
||||
TOGGLE_FILMSTRIP: 'UI.toggle_filmstrip',
|
||||
|
||||
HANGUP: 'UI.hangup',
|
||||
VIDEO_DEVICE_CHANGED: 'UI.video_device_changed',
|
||||
AUDIO_DEVICE_CHANGED: 'UI.audio_device_changed',
|
||||
|
||||
/**
|
||||
* Notifies that the side toolbar container has been toggled. The actual
|
||||
* event must contain the identifier of the container that has been toggled
|
||||
* and information about toggle on or off.
|
||||
*/
|
||||
SIDE_TOOLBAR_CONTAINER_TOGGLED: 'UI.side_container_toggled',
|
||||
|
||||
/**
|
||||
* Notifies that the raise hand has been changed.
|
||||
*/
|
||||
LOCAL_RAISE_HAND_CHANGED: 'UI.local_raise_hand_changed'
|
||||
};
|
||||
@@ -1,15 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<!--#include virtual="/base.html" -->
|
||||
<link rel="stylesheet" href="css/all.css"/>
|
||||
<!--#include virtual="/title.html" -->
|
||||
</head>
|
||||
<body>
|
||||
<div class="redirectPageMessage">
|
||||
Sorry! You are not allowed to be here :(
|
||||
<div class="forbidden-msg">
|
||||
<p>You might be missing the JWT or using an incompatible one.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user