mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-06-01 10:17:47 +00:00
Compare commits
131 Commits
5472
...
saghul-pat
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ef97f2708 | ||
|
|
10d54498d0 | ||
|
|
cd166b5f19 | ||
|
|
b5faf9f62a | ||
|
|
d98ea3ca24 | ||
|
|
d2b7151f23 | ||
|
|
7fa72db384 | ||
|
|
d56b282c5d | ||
|
|
72e1dcb877 | ||
|
|
98b05a2529 | ||
|
|
2d0a70f2ab | ||
|
|
6243836b3e | ||
|
|
6520cc2496 | ||
|
|
61684b1071 | ||
|
|
e42db3c9c2 | ||
|
|
6f75226044 | ||
|
|
7e9d665697 | ||
|
|
9aa2bbe115 | ||
|
|
31e4a9c1a0 | ||
|
|
1106e71f03 | ||
|
|
d84ba85f58 | ||
|
|
1e706db114 | ||
|
|
c82cead5ae | ||
|
|
39d1ccff85 | ||
|
|
2e69ec71c5 | ||
|
|
be6adad61b | ||
|
|
23b8dd4860 | ||
|
|
c74bdf137c | ||
|
|
8ebca64af1 | ||
|
|
61025edb74 | ||
|
|
4e2fea1e12 | ||
|
|
f5cdd5fca1 | ||
|
|
eec8b9e58e | ||
|
|
a96a4ea336 | ||
|
|
876773ca2a | ||
|
|
bc6070b98d | ||
|
|
9ca0ee41b6 | ||
|
|
957bd8916a | ||
|
|
8f08a54fb2 | ||
|
|
28c793b3a9 | ||
|
|
243aa20912 | ||
|
|
2d27195652 | ||
|
|
bcb0fe919c | ||
|
|
981fc2f410 | ||
|
|
e6e808c764 | ||
|
|
01ae4c477b | ||
|
|
e0010def14 | ||
|
|
e77216f18e | ||
|
|
ba7b5fc996 | ||
|
|
162a67fe8b | ||
|
|
0aba61d5c6 | ||
|
|
5e4f09dd0a | ||
|
|
17c0298e59 | ||
|
|
6348840cbd | ||
|
|
6a7842ab7e | ||
|
|
b2894cc941 | ||
|
|
fc52105cf6 | ||
|
|
b47a263e5c | ||
|
|
15b677aa2a | ||
|
|
08fb232627 | ||
|
|
3c5017a66a | ||
|
|
a97b700712 | ||
|
|
bf36b22bbd | ||
|
|
d6253c57c9 | ||
|
|
978586c157 | ||
|
|
a7efbfb0f8 | ||
|
|
68eb0795eb | ||
|
|
4c46396e6a | ||
|
|
4767ef497f | ||
|
|
f27cb46f3c | ||
|
|
c2e55339d1 | ||
|
|
a78c8c199d | ||
|
|
0d7ecfad40 | ||
|
|
040a1d8add | ||
|
|
3294dc882d | ||
|
|
21d5b7bcd6 | ||
|
|
c4cbf83208 | ||
|
|
f1ae9a6697 | ||
|
|
c653334d69 | ||
|
|
e51655a93a | ||
|
|
8983ea41fd | ||
|
|
cd6a814978 | ||
|
|
41f5872f70 | ||
|
|
af01072827 | ||
|
|
eaa084722f | ||
|
|
318bc26fa0 | ||
|
|
072c29c0f9 | ||
|
|
9ab16a0a5c | ||
|
|
69d2cb52fe | ||
|
|
6845a759a0 | ||
|
|
7aca5e71b9 | ||
|
|
78e825de36 | ||
|
|
829f36e886 | ||
|
|
afbf261f67 | ||
|
|
76fc5a0806 | ||
|
|
09d3344c39 | ||
|
|
0dc061f633 | ||
|
|
ba110fbdc3 | ||
|
|
a6167997d8 | ||
|
|
b6047b9761 | ||
|
|
53a05dd1ad | ||
|
|
be5c397422 | ||
|
|
d38bf36b2a | ||
|
|
1793bf460e | ||
|
|
9e3084ef48 | ||
|
|
b0f8b34d94 | ||
|
|
366dc8d11b | ||
|
|
667a6eac80 | ||
|
|
84f37b1777 | ||
|
|
779d44298b | ||
|
|
b4ba887d92 | ||
|
|
0182cc0504 | ||
|
|
1dbfbb9786 | ||
|
|
d77a3bb61e | ||
|
|
9b6b335c60 | ||
|
|
2ab0d6b606 | ||
|
|
dec05917d3 | ||
|
|
0715f85796 | ||
|
|
f1e13404b7 | ||
|
|
dd89034503 | ||
|
|
92c34c9c3e | ||
|
|
e273a05dd0 | ||
|
|
81dfbaeb81 | ||
|
|
2cfa5f6312 | ||
|
|
162ec5a2b2 | ||
|
|
338ff43c81 | ||
|
|
da5603dd9a | ||
|
|
92c6324ff3 | ||
|
|
4b7a6741fa | ||
|
|
f435fc4ade | ||
|
|
b250f21f91 |
@@ -8,8 +8,12 @@ libs/*
|
||||
resources/*
|
||||
react/features/stream-effects/virtual-background/vendor/*
|
||||
load-test/*
|
||||
react/features/facial-recognition/resources/*
|
||||
|
||||
# ESLint will by default ignore its own configuration file. However, there does
|
||||
# not seem to be a reason why we will want to risk being inconsistent with our
|
||||
# remaining JavaScript source code.
|
||||
!.eslintrc.js
|
||||
|
||||
# Not worth it.
|
||||
actionTypes.js
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
module.exports = {
|
||||
'extends': [
|
||||
'eslint-config-jitsi'
|
||||
'@jitsi/eslint-config'
|
||||
]
|
||||
};
|
||||
|
||||
3
.github/workflows/ci.yml
vendored
3
.github/workflows/ci.yml
vendored
@@ -10,8 +10,7 @@ jobs:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '14.x'
|
||||
- run: npm i -g npm@7
|
||||
node-version: '16.x'
|
||||
- run: npm install
|
||||
- name: Check git status
|
||||
run: git status
|
||||
|
||||
32
Makefile
32
Makefile
@@ -7,6 +7,7 @@ OLM_DIR = node_modules/@matrix-org/olm
|
||||
RNNOISE_WASM_DIR = node_modules/rnnoise-wasm/dist/
|
||||
TFLITE_WASM = react/features/stream-effects/virtual-background/vendor/tflite
|
||||
MEET_MODELS_DIR = react/features/stream-effects/virtual-background/vendor/models/
|
||||
FACIAL_MODELS_DIR = react/features/facial-recognition/resources
|
||||
NODE_SASS = ./node_modules/.bin/sass
|
||||
NPM = npm
|
||||
OUTPUT_DIR = .
|
||||
@@ -28,7 +29,7 @@ clean:
|
||||
rm -fr $(BUILD_DIR)
|
||||
|
||||
.NOTPARALLEL:
|
||||
deploy: deploy-init deploy-appbundle deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-libflac deploy-olm deploy-css deploy-local
|
||||
deploy: deploy-init deploy-appbundle deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-libflac deploy-olm deploy-css deploy-local deploy-facial-expressions
|
||||
|
||||
deploy-init:
|
||||
rm -fr $(DEPLOY_DIR)
|
||||
@@ -37,24 +38,26 @@ deploy-init:
|
||||
deploy-appbundle:
|
||||
cp \
|
||||
$(BUILD_DIR)/app.bundle.min.js \
|
||||
$(BUILD_DIR)/app.bundle.min.map \
|
||||
$(BUILD_DIR)/app.bundle.min.js.map \
|
||||
$(BUILD_DIR)/do_external_connect.min.js \
|
||||
$(BUILD_DIR)/do_external_connect.min.map \
|
||||
$(BUILD_DIR)/do_external_connect.min.js.map \
|
||||
$(BUILD_DIR)/external_api.min.js \
|
||||
$(BUILD_DIR)/external_api.min.map \
|
||||
$(BUILD_DIR)/external_api.min.js.map \
|
||||
$(BUILD_DIR)/flacEncodeWorker.min.js \
|
||||
$(BUILD_DIR)/flacEncodeWorker.min.map \
|
||||
$(BUILD_DIR)/flacEncodeWorker.min.js.map \
|
||||
$(BUILD_DIR)/dial_in_info_bundle.min.js \
|
||||
$(BUILD_DIR)/dial_in_info_bundle.min.map \
|
||||
$(BUILD_DIR)/dial_in_info_bundle.min.js.map \
|
||||
$(BUILD_DIR)/alwaysontop.min.js \
|
||||
$(BUILD_DIR)/alwaysontop.min.map \
|
||||
$(BUILD_DIR)/alwaysontop.min.js.map \
|
||||
$(OUTPUT_DIR)/analytics-ga.js \
|
||||
$(BUILD_DIR)/analytics-ga.min.js \
|
||||
$(BUILD_DIR)/analytics-ga.min.map \
|
||||
$(BUILD_DIR)/analytics-ga.min.js.map \
|
||||
$(BUILD_DIR)/facial-expressions-worker.min.js \
|
||||
$(BUILD_DIR)/facial-expressions-worker.min.js.map \
|
||||
$(DEPLOY_DIR)
|
||||
cp \
|
||||
$(BUILD_DIR)/close3.min.js \
|
||||
$(BUILD_DIR)/close3.min.map \
|
||||
$(BUILD_DIR)/close3.min.js.map \
|
||||
$(DEPLOY_DIR) || true
|
||||
|
||||
deploy-lib-jitsi-meet:
|
||||
@@ -85,12 +88,17 @@ deploy-rnnoise-binary:
|
||||
deploy-tflite:
|
||||
cp \
|
||||
$(TFLITE_WASM)/*.wasm \
|
||||
$(DEPLOY_DIR)
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-meet-models:
|
||||
cp \
|
||||
$(MEET_MODELS_DIR)/*.tflite \
|
||||
$(DEPLOY_DIR)
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-facial-expressions:
|
||||
cp \
|
||||
$(FACIAL_MODELS_DIR)/* \
|
||||
$(DEPLOY_DIR)
|
||||
|
||||
deploy-css:
|
||||
$(NODE_SASS) $(STYLES_MAIN) $(STYLES_BUNDLE) && \
|
||||
@@ -101,7 +109,7 @@ deploy-local:
|
||||
([ ! -x deploy-local.sh ] || ./deploy-local.sh)
|
||||
|
||||
.NOTPARALLEL:
|
||||
dev: deploy-init deploy-css deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-libflac deploy-olm
|
||||
dev: deploy-init deploy-css deploy-rnnoise-binary deploy-tflite deploy-meet-models deploy-lib-jitsi-meet deploy-libflac deploy-olm deploy-facial-expressions
|
||||
$(WEBPACK_DEV_SERVER)
|
||||
|
||||
source-package:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# <p align="center">Jitsi Meet</p>
|
||||
|
||||
Jitsi Meet is a set of Open Source projects which empower users to use and deploy
|
||||
aaaJitsi Meet is a set of Open Source projects which empower users to use and deploy
|
||||
video conferencing platforms with state-of-the-art video quality and features.
|
||||
|
||||
<hr />
|
||||
|
||||
@@ -71,6 +71,7 @@ dependencies {
|
||||
implementation project(':react-native-community_netinfo')
|
||||
implementation project(':react-native-default-preference')
|
||||
implementation project(':react-native-gesture-handler')
|
||||
implementation project(':react-native-get-random-values')
|
||||
implementation project(':react-native-immersive')
|
||||
implementation project(':react-native-keep-awake')
|
||||
implementation project(':react-native-masked-view_masked-view')
|
||||
|
||||
@@ -190,6 +190,7 @@ class ReactInstanceManagerHolder {
|
||||
new com.kevinresol.react_native_default_preference.RNDefaultPreferencePackage(),
|
||||
new com.learnium.RNDeviceInfo.RNDeviceInfo(),
|
||||
new com.swmansion.gesturehandler.react.RNGestureHandlerPackage(),
|
||||
new org.linusu.RNGetRandomValuesPackage(),
|
||||
new com.rnimmersive.RNImmersivePackage(),
|
||||
new com.swmansion.rnscreens.RNScreensPackage(),
|
||||
new com.zmxv.RNSound.RNSoundPackage(),
|
||||
|
||||
@@ -17,6 +17,8 @@ include ':react-native-device-info'
|
||||
project(':react-native-device-info').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-device-info/android')
|
||||
include ':react-native-gesture-handler'
|
||||
project(':react-native-gesture-handler').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-gesture-handler/android')
|
||||
include ':react-native-get-random-values'
|
||||
project(':react-native-get-random-values').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-get-random-values/android')
|
||||
include ':react-native-google-signin'
|
||||
project(':react-native-google-signin').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/google-signin/android')
|
||||
include ':react-native-immersive'
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/* global APP, JitsiMeetJS, config, interfaceConfig */
|
||||
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
import Logger from '@jitsi/logger';
|
||||
import EventEmitter from 'events';
|
||||
import Logger from 'jitsi-meet-logger';
|
||||
|
||||
import { openConnection } from './connection';
|
||||
import { ENDPOINT_TEXT_MESSAGE_NAME } from './modules/API/constants';
|
||||
@@ -29,6 +29,7 @@ import { shouldShowModeratedNotification } from './react/features/av-moderation/
|
||||
import {
|
||||
AVATAR_URL_COMMAND,
|
||||
EMAIL_COMMAND,
|
||||
_conferenceWillJoin,
|
||||
authStatusChanged,
|
||||
commonUserJoinedHandling,
|
||||
commonUserLeftHandling,
|
||||
@@ -47,7 +48,7 @@ import {
|
||||
onStartMutedPolicyChanged,
|
||||
p2pStatusChanged,
|
||||
sendLocalParticipant,
|
||||
_conferenceWillJoin
|
||||
nonParticipantMessageReceived
|
||||
} from './react/features/base/conference';
|
||||
import { getReplaceParticipant } from './react/features/base/config/functions';
|
||||
import {
|
||||
@@ -107,6 +108,7 @@ import {
|
||||
getLocalJitsiAudioTrack,
|
||||
getLocalJitsiVideoTrack,
|
||||
getLocalTracks,
|
||||
getLocalVideoTrack,
|
||||
isLocalCameraTrackMuted,
|
||||
isLocalTrackMuted,
|
||||
isUserInteractionRequiredForUnmute,
|
||||
@@ -759,7 +761,7 @@ export default {
|
||||
// XXX The API will take care of disconnecting from the XMPP
|
||||
// server (and, thus, leaving the room) on unload.
|
||||
return new Promise((resolve, reject) => {
|
||||
(new ConferenceConnector(resolve, reject)).connect();
|
||||
new ConferenceConnector(resolve, reject).connect();
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1014,6 +1016,14 @@ export default {
|
||||
* dialogs in case of media permissions error.
|
||||
*/
|
||||
muteVideo(mute, showUI = true) {
|
||||
if (this.videoSwitchInProgress) {
|
||||
// Turning the camera on while the screen sharing mode is being turned off is causing issues around
|
||||
// the presenter mode handling. It should be okay for the user to click the button again once that's done.
|
||||
console.warn('muteVideo - unable to perform operations while video switch is in progress');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mute
|
||||
&& isUserInteractionRequiredForUnmute(APP.store.getState())) {
|
||||
logger.error('Unmuting video requires user interaction');
|
||||
@@ -1351,11 +1361,49 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
_createRoom(localTracks) {
|
||||
/**
|
||||
* Used by the Breakout Rooms feature to join a breakout room or go back to the main room.
|
||||
*/
|
||||
async joinRoom(roomName, isBreakoutRoom = false) {
|
||||
this.roomName = roomName;
|
||||
|
||||
const { tryCreateLocalTracks, errors } = this.createInitialLocalTracks();
|
||||
const localTracks = await tryCreateLocalTracks;
|
||||
|
||||
this._displayErrorsForCreateInitialLocalTracks(errors);
|
||||
localTracks.forEach(track => {
|
||||
if ((track.isAudioTrack() && this.isLocalAudioMuted())
|
||||
|| (track.isVideoTrack() && this.isLocalVideoMuted())) {
|
||||
track.mute();
|
||||
}
|
||||
});
|
||||
this._createRoom(localTracks, isBreakoutRoom);
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
new ConferenceConnector(resolve, reject).connect();
|
||||
});
|
||||
},
|
||||
|
||||
_createRoom(localTracks, isBreakoutRoom = false) {
|
||||
const extraOptions = {};
|
||||
|
||||
if (isBreakoutRoom) {
|
||||
// We must be in a room already.
|
||||
if (!room?.xmpp?.breakoutRoomsComponentAddress) {
|
||||
throw new Error('Breakout Rooms not enabled');
|
||||
}
|
||||
|
||||
// TODO: re-evaluate this. -saghul
|
||||
extraOptions.customDomain = room.xmpp.breakoutRoomsComponentAddress;
|
||||
}
|
||||
|
||||
room
|
||||
= connection.initJitsiConference(
|
||||
APP.conference.roomName,
|
||||
this._getConferenceOptions());
|
||||
{
|
||||
...this._getConferenceOptions(),
|
||||
...extraOptions
|
||||
});
|
||||
|
||||
// Filter out the tracks that are muted (except on Safari).
|
||||
const tracks = browser.isWebKitBased() ? localTracks : localTracks.filter(track => !track.isMuted());
|
||||
@@ -1549,6 +1597,8 @@ export default {
|
||||
if (config.enableScreenshotCapture) {
|
||||
APP.store.dispatch(toggleScreenshotCaptureSummary(false));
|
||||
}
|
||||
const tracks = APP.store.getState()['features/base/tracks'];
|
||||
const duration = getLocalVideoTrack(tracks)?.jitsiTrack.getDuration() ?? 0;
|
||||
|
||||
// It can happen that presenter GUM is in progress while screensharing is being turned off. Here it needs to
|
||||
// wait for that GUM to be resolved in order to prevent leaking the presenter track(this.localPresenterVideo
|
||||
@@ -1610,7 +1660,8 @@ export default {
|
||||
return promise.then(
|
||||
() => {
|
||||
this.videoSwitchInProgress = false;
|
||||
sendAnalytics(createScreenSharingEvent('stopped'));
|
||||
sendAnalytics(createScreenSharingEvent('stopped',
|
||||
duration === 0 ? null : duration));
|
||||
logger.info('Screen sharing stopped.');
|
||||
},
|
||||
error => {
|
||||
@@ -1640,7 +1691,7 @@ export default {
|
||||
async toggleScreenSharing(toggle = !this._untoggleScreenSharing, options = {}, ignoreDidHaveVideo) {
|
||||
logger.debug(`toggleScreenSharing: ${toggle}`);
|
||||
if (this.videoSwitchInProgress) {
|
||||
return Promise.reject('Switch in progress.');
|
||||
return Promise.reject(`toggleScreenSharing: ${toggle} aborted - video switch in progress.`);
|
||||
}
|
||||
if (!JitsiMeetJS.isDesktopSharingEnabled()) {
|
||||
return Promise.reject('Cannot toggle screen sharing: not supported.');
|
||||
@@ -2210,6 +2261,10 @@ export default {
|
||||
}
|
||||
});
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.NON_PARTICIPANT_MESSAGE_RECEIVED,
|
||||
(...args) => APP.store.dispatch(nonParticipantMessageReceived(...args)));
|
||||
|
||||
room.on(
|
||||
JitsiConferenceEvents.LOCK_STATE_CHANGED,
|
||||
(...args) => APP.store.dispatch(lockStateChanged(room, ...args)));
|
||||
@@ -2638,12 +2693,17 @@ export default {
|
||||
|
||||
APP.store.dispatch(updateDeviceList(devices));
|
||||
|
||||
// Firefox users can choose their preferred device in the gUM prompt. In that case
|
||||
// we should respect that and not attempt to switch to the preferred device from
|
||||
// our settings.
|
||||
const newLabelsOnly = mediaDeviceHelper.newDeviceListAddedLabelsOnly(oldDevices, devices);
|
||||
const newDevices
|
||||
= mediaDeviceHelper.getNewMediaDevicesAfterDeviceListChanged(
|
||||
devices,
|
||||
this.isSharingScreen,
|
||||
localVideo,
|
||||
localAudio);
|
||||
localAudio,
|
||||
newLabelsOnly);
|
||||
const promises = [];
|
||||
const audioWasMuted = this.isLocalAudioMuted();
|
||||
const videoWasMuted = this.isLocalVideoMuted();
|
||||
@@ -2887,6 +2947,17 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Leaves the room.
|
||||
*
|
||||
* @returns {Promise}
|
||||
*/
|
||||
leaveRoom() {
|
||||
if (room && room.isJoined()) {
|
||||
return room.leave();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Leaves the room and calls JitsiConnection.disconnect.
|
||||
*
|
||||
@@ -2981,6 +3052,15 @@ export default {
|
||||
room.sendEndpointMessage(to, payload);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sends a facial expression as a string and its duration as a number
|
||||
* @param {object} payload - Object containing the {string} facialExpression
|
||||
* and {number} duration
|
||||
*/
|
||||
sendFacialExpression(payload) {
|
||||
room.sendFacialExpression(payload);
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds new listener.
|
||||
* @param {String} eventName the name of the event
|
||||
|
||||
85
config.js
85
config.js
@@ -279,6 +279,7 @@ var config = {
|
||||
// autoHide: true,
|
||||
// autoHideTimeout: 5000,
|
||||
// disabled: false,
|
||||
// disableDetails: false,
|
||||
// inactiveDisabled: false
|
||||
// },
|
||||
|
||||
@@ -429,6 +430,12 @@ var config = {
|
||||
// Hides lobby button
|
||||
// hideLobbyButton: false,
|
||||
|
||||
// If Lobby is enabled starts knocking automatically.
|
||||
// autoKnockLobby: false,
|
||||
|
||||
// Hides add breakout room button
|
||||
// hideAddRoomButton: false,
|
||||
|
||||
// Require users to always specify a display name.
|
||||
// requireDisplayName: true,
|
||||
|
||||
@@ -648,6 +655,9 @@ var config = {
|
||||
// Enables sending participants' emails (if available) to callstats and other analytics
|
||||
// enableEmailInStats: false,
|
||||
|
||||
// Enables detecting faces of participants and get their expression and send it to other participants
|
||||
// enableFacialRecognition: true,
|
||||
|
||||
// Controls the percentage of automatic feedback shown to participants when callstats is enabled.
|
||||
// The default value is 100%. If set to 0, no automatic feedback will be requested
|
||||
// feedbackPercentage: 100,
|
||||
@@ -825,6 +835,10 @@ var config = {
|
||||
// format: 'flac'
|
||||
//
|
||||
|
||||
// },
|
||||
// e2ee: {
|
||||
// labels,
|
||||
// externallyManagedKey: false
|
||||
// },
|
||||
|
||||
// Options related to end-to-end (participant to participant) ping.
|
||||
@@ -895,25 +909,55 @@ var config = {
|
||||
If there is no url set or there are missing fields, the defaults are applied.
|
||||
The config file should be in JSON.
|
||||
None of the fields are mandatory and the response must have the shape:
|
||||
{
|
||||
// The domain url to apply (will replace the domain in the sharing conference link/embed section)
|
||||
inviteDomain: 'example-company.org,
|
||||
// The hex value for the colour used as background
|
||||
backgroundColor: '#fff',
|
||||
// The url for the image used as background
|
||||
backgroundImageUrl: 'https://example.com/background-img.png',
|
||||
// The anchor url used when clicking the logo image
|
||||
logoClickUrl: 'https://example-company.org',
|
||||
// The url used for the image used as logo
|
||||
logoImageUrl: 'https://example.com/logo-img.png',
|
||||
// Overwrite for pool of background images for avatars
|
||||
avatarBackgrounds: ['url(https://example.com/avatar-background-1.png)', '#FFF'],
|
||||
// The lobby/prejoin screen background
|
||||
premeetingBackground: 'url(https://example.com/premeeting-background.png)',
|
||||
// A list of images that can be used as video backgrounds.
|
||||
// When this field is present, the default images will be replaced with those provided.
|
||||
virtualBackgrounds: ['https://example.com/img.jpg']
|
||||
}
|
||||
{
|
||||
// The domain url to apply (will replace the domain in the sharing conference link/embed section)
|
||||
inviteDomain: 'example-company.org,
|
||||
// The hex value for the colour used as background
|
||||
backgroundColor: '#fff',
|
||||
// The url for the image used as background
|
||||
backgroundImageUrl: 'https://example.com/background-img.png',
|
||||
// The anchor url used when clicking the logo image
|
||||
logoClickUrl: 'https://example-company.org',
|
||||
// The url used for the image used as logo
|
||||
logoImageUrl: 'https://example.com/logo-img.png',
|
||||
// Overwrite for pool of background images for avatars
|
||||
avatarBackgrounds: ['url(https://example.com/avatar-background-1.png)', '#FFF'],
|
||||
// The lobby/prejoin screen background
|
||||
premeetingBackground: 'url(https://example.com/premeeting-background.png)',
|
||||
// A list of images that can be used as video backgrounds.
|
||||
// When this field is present, the default images will be replaced with those provided.
|
||||
virtualBackgrounds: ['https://example.com/img.jpg'],
|
||||
// Object containing a theme's properties. It also supports partial overwrites of the main theme.
|
||||
// For a list of all possible theme tokens and their current defaults, please check:
|
||||
// https://github.com/jitsi/jitsi-meet/tree/master/resources/custom-theme/custom-theme.json
|
||||
// For a short explanations on each of the tokens, please check:
|
||||
// https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/ui/Tokens.js
|
||||
// IMPORTANT!: This is work in progress so many of the various tokens are not yet applied in code
|
||||
// or they are partially applied.
|
||||
customTheme: {
|
||||
palette: {
|
||||
ui01: "orange !important",
|
||||
ui02: "maroon",
|
||||
surface02: 'darkgreen',
|
||||
ui03: "violet",
|
||||
ui04: "magenta",
|
||||
ui05: "blueviolet",
|
||||
field02Hover: 'red',
|
||||
action01: 'green',
|
||||
action01Hover: 'lightgreen',
|
||||
action02Disabled: 'beige',
|
||||
success02: 'cadetblue',
|
||||
action02Hover: 'aliceblue'
|
||||
},
|
||||
typography: {
|
||||
labelRegular: {
|
||||
fontSize: 25,
|
||||
lineHeight: 30,
|
||||
fontWeight: 500
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
// dynamicBrandingUrl: '',
|
||||
|
||||
@@ -921,6 +965,9 @@ var config = {
|
||||
// Only the default ones from will be available.
|
||||
// disableAddingBackgroundImages: false,
|
||||
|
||||
// Disables using screensharing as virtual background.
|
||||
// disableScreensharingVirtualBackground: false,
|
||||
|
||||
// Sets the background transparency level. '0' is fully transparent, '1' is opaque.
|
||||
// backgroundAlpha: 1,
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* global APP, JitsiMeetJS, config */
|
||||
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
import Logger from 'jitsi-meet-logger';
|
||||
import Logger from '@jitsi/logger';
|
||||
|
||||
import { redirectToTokenAuthService } from './modules/UI/authentication/AuthHandler';
|
||||
import { LoginDialog } from './react/features/authentication/components';
|
||||
|
||||
@@ -103,3 +103,20 @@ div.Tooltip {
|
||||
line-height: 14px;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
// make modal full screen on landscape orientation
|
||||
@media (max-height: 420px) {
|
||||
.atlaskit-portal {
|
||||
.css-1oc7v0j {
|
||||
height: 100%;
|
||||
padding: 0;
|
||||
max-width: 100%;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
|
||||
&> div {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,34 +2,39 @@
|
||||
background-color: $chatBackgroundColor;
|
||||
box-sizing: border-box;
|
||||
color: #FFF;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
left: -$sidebarWidth;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: $sidebarWidth;
|
||||
z-index: $sideToolbarContainerZ;
|
||||
|
||||
/**
|
||||
* The sidebar (chat) is off-screen when hidden. Move it flush to the left
|
||||
* side of the window when it should be visible.
|
||||
*/
|
||||
&.slideInExt {
|
||||
left: 0;
|
||||
@media (max-width: 580px) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.chat-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
// extract header + tabs height
|
||||
height: calc(100% - 102px);
|
||||
}
|
||||
|
||||
.chat-panel-no-tabs {
|
||||
// extract header height
|
||||
height: calc(100% - 70px);
|
||||
}
|
||||
|
||||
#chatconversation {
|
||||
box-sizing: border-box;
|
||||
flex: 1;
|
||||
font-size: 10pt;
|
||||
// extract message input height
|
||||
height: calc(100% - 68px);
|
||||
line-height: 20px;
|
||||
overflow: auto;
|
||||
padding: 16px;
|
||||
text-align: left;
|
||||
width: $sidebarWidth;
|
||||
word-wrap: break-word;
|
||||
|
||||
display: flex;
|
||||
@@ -58,28 +63,6 @@
|
||||
a:active {
|
||||
color: black;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
background: #06a5df;
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track {
|
||||
background: black;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-track-piece {
|
||||
background: black;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #06a5df;
|
||||
border-radius: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
#chat-recipient {
|
||||
@@ -319,10 +302,6 @@
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@media (max-width: 580px) {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.sr-only {
|
||||
|
||||
@@ -11,12 +11,6 @@
|
||||
{
|
||||
@extend %connection-info;
|
||||
|
||||
/**
|
||||
* Apply negative margin to reduce the appearance of padding in AtlasKit
|
||||
* InlineDialog.
|
||||
*/
|
||||
margin: -15px;
|
||||
|
||||
> table {
|
||||
white-space: nowrap;
|
||||
@extend %connection-info;
|
||||
|
||||
@@ -56,3 +56,9 @@
|
||||
border-radius: 3px;
|
||||
}
|
||||
}
|
||||
|
||||
.mobile-browser.shift-right {
|
||||
.participants_pane {
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,12 @@
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.poll-creator {
|
||||
color: #C2C2C2;
|
||||
font-weight: 600;
|
||||
margin: 4px 0 16px 0;
|
||||
}
|
||||
|
||||
.poll-answer-container {
|
||||
background: #3D3D3D;
|
||||
border-radius: 3px;
|
||||
@@ -134,7 +140,7 @@ ol.poll-result-list {
|
||||
.poll-question {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 16px;
|
||||
line-height: 26px;
|
||||
}
|
||||
|
||||
.poll-answer-voters {
|
||||
@@ -236,12 +242,8 @@ ol.poll-result-list {
|
||||
|
||||
|
||||
.polls-pane-content {
|
||||
height: calc(100% - 102px);
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
@media (max-width: 580px) {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.pane-content{
|
||||
@@ -312,6 +314,10 @@ ol.poll-result-list {
|
||||
padding: 10px 16px;
|
||||
}
|
||||
|
||||
#polls-panel {
|
||||
height: calc(100% - 102px);
|
||||
}
|
||||
|
||||
.poll-container {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
|
||||
@@ -46,3 +46,7 @@
|
||||
padding: 16px 24px;
|
||||
z-index: $popoverZ;
|
||||
}
|
||||
|
||||
.padded-content {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
.popupmenu {
|
||||
background-color: $menuBG;
|
||||
border-radius: 3px;
|
||||
list-style-type: none;
|
||||
min-width: 150px;
|
||||
text-align: left;
|
||||
padding: 0px;
|
||||
@@ -38,6 +39,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&__text {
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
|
||||
@@ -77,14 +77,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.toolbox-button {
|
||||
color: $toolbarButtonColor;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
line-height: $newToolbarSize;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.toolbar-button-with-badge {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
@@ -115,86 +107,6 @@
|
||||
padding-bottom: env(safe-area-inset-bottom, 0);
|
||||
}
|
||||
|
||||
.toolbox-content-items {
|
||||
background: $newToolbarBackgroundColor;
|
||||
border-radius: 6px;
|
||||
margin: 0 auto;
|
||||
padding: 6px;
|
||||
text-align: center;
|
||||
pointer-events: all;
|
||||
box-shadow: 0px 2px 8px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.15);
|
||||
|
||||
>div {
|
||||
margin-left: 8px;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.overflow-menu {
|
||||
font-size: 14px;
|
||||
list-style-type: none;
|
||||
padding: 8px 0;
|
||||
background-color: $menuBG;
|
||||
|
||||
.profile-text {
|
||||
max-width: 150px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.overflow-menu-item {
|
||||
align-items: center;
|
||||
color: $overflowMenuItemColor;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
height: 40px;
|
||||
line-height: 24px;
|
||||
padding: 8px 16px;
|
||||
box-sizing: border-box;
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background: $overflowMenuItemBackground;
|
||||
}
|
||||
}
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&.unclickable {
|
||||
cursor: default;
|
||||
}
|
||||
&.disabled {
|
||||
cursor: initial;
|
||||
color: #929292;
|
||||
|
||||
&:hover {
|
||||
background: none;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: #929292;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&.unclickable:hover {
|
||||
background: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.beta-tag {
|
||||
background: #36383C;
|
||||
border-radius: 3px;
|
||||
@@ -205,73 +117,12 @@
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.overflow-menu-item-icon {
|
||||
margin-right: 16px;
|
||||
|
||||
i {
|
||||
display: inline;
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
i:hover {
|
||||
background-color: initial;
|
||||
}
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 24px;
|
||||
max-height: 24px;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: #fff;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.overflow-menu-hr {
|
||||
border-top: 1px solid #4C4D50;
|
||||
border-bottom: 0;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.toolbox-icon {
|
||||
display: flex;
|
||||
border-radius: 3px;
|
||||
flex-direction: column;
|
||||
font-size: 24px;
|
||||
height: $newToolbarSize;
|
||||
justify-content: center;
|
||||
width: $newToolbarSize;
|
||||
|
||||
@media (hover: hover) and (pointer: fine) {
|
||||
&:hover {
|
||||
background: $newToolbarButtonHoverColor;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 320px) {
|
||||
height: 36px;
|
||||
width: 36px;
|
||||
}
|
||||
|
||||
&.toggled {
|
||||
background: $newToolbarButtonToggleColor;
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
cursor: initial !important;
|
||||
background-color: #36383c !important;
|
||||
|
||||
svg {
|
||||
fill: #929292 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.hangup-button {
|
||||
background-color: $hangupColor;
|
||||
|
||||
|
||||
@@ -122,7 +122,7 @@ $zindex10: 10;
|
||||
$reloadZ: 20;
|
||||
$poweredByZ: 100;
|
||||
$ringingZ: 300;
|
||||
$sideToolbarContainerZ: 200;
|
||||
$sideToolbarContainerZ: 300;
|
||||
$toolbarZ: 250;
|
||||
$drawerZ: 351;
|
||||
$tooltipsZ: 401;
|
||||
@@ -269,4 +269,4 @@ $verySmallScreen: 500px;
|
||||
* Prejoin / premeeting screen
|
||||
*/
|
||||
|
||||
$prejoinDefaultContentWidth: 336px;
|
||||
$prejoinDefaultContentWidth: 336px;
|
||||
|
||||
@@ -87,7 +87,6 @@
|
||||
|
||||
&__toolbar {
|
||||
bottom: 0;
|
||||
height: $thumbnailToolbarHeight;
|
||||
padding: 0 5px 0 5px;
|
||||
}
|
||||
|
||||
@@ -190,6 +189,20 @@
|
||||
z-index: $zindex2;
|
||||
}
|
||||
|
||||
&__participant-name {
|
||||
color: #fff;
|
||||
background-color: rgba(0,0,0,.4);
|
||||
padding: 3px 7px;
|
||||
border-radius: 3px;
|
||||
max-width: calc(100% - 32px);
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
height: 16px;
|
||||
display: inline-block;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
@media (min-width: 581px) {
|
||||
&.shift-right {
|
||||
&#largeVideoContainer {
|
||||
@@ -281,21 +294,8 @@
|
||||
#alwaysOnTop .displayname,
|
||||
.videocontainer .displayname,
|
||||
.videocontainer .editdisplayname {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
left: 10%;
|
||||
width: 80%;
|
||||
top: 50%;
|
||||
@include transform(translateY(-40%));
|
||||
color: $participantNameColor;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 12px;
|
||||
font-weight: 100;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
line-height: $thumbnailToolbarHeight;
|
||||
z-index: $zindex2;
|
||||
color: $participantNameColor;
|
||||
}
|
||||
|
||||
#alwaysOnTop .displayname {
|
||||
@@ -415,6 +415,11 @@
|
||||
&.status-other {
|
||||
background: $connectionIndicatorBg;
|
||||
}
|
||||
|
||||
&.status-disabled {
|
||||
background: transparent;
|
||||
border: none
|
||||
}
|
||||
}
|
||||
|
||||
.local-video-menu-trigger,
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
.copy-button {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 8px 8px 16px;
|
||||
margin-top: 5px;
|
||||
width: calc(100% - 24px);
|
||||
height: 24px;
|
||||
|
||||
background: #0376DA;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
background: #278ADF;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
&-content {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
max-width: 292px;
|
||||
margin-right: 16px;
|
||||
|
||||
&.selected {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
&.clicked {
|
||||
background: #31B76A;
|
||||
}
|
||||
|
||||
& > div > svg > path {
|
||||
fill: #fff;
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,6 @@ $flagsImagePath: "../images/";
|
||||
@import 'inlay';
|
||||
@import 'reload_overlay/reload_overlay';
|
||||
@import 'mini_toolbox';
|
||||
@import 'buttons/copy.scss';
|
||||
@import 'modals/desktop-picker/desktop-picker';
|
||||
@import 'modals/device-selection/device-selection';
|
||||
@import 'modals/dialog';
|
||||
|
||||
@@ -27,7 +27,10 @@
|
||||
|
||||
.speaker-stats-item__status,
|
||||
.speaker-stats-item__name,
|
||||
.speaker-stats-item__time {
|
||||
.speaker-stats-item__time,
|
||||
.speaker-stats-item__name_expressions_on,
|
||||
.speaker-stats-item__time_expressions_on,
|
||||
.speaker-stats-item__expression {
|
||||
display: inline-block;
|
||||
margin: 5px 0;
|
||||
vertical-align: middle;
|
||||
@@ -41,9 +44,35 @@
|
||||
.speaker-stats-item__time {
|
||||
width: 55%;
|
||||
}
|
||||
.speaker-stats-item__name_expressions_on {
|
||||
width: 20%;
|
||||
}
|
||||
.speaker-stats-item__time_expressions_on {
|
||||
width: 25%;
|
||||
}
|
||||
|
||||
.speaker-stats-item__expression {
|
||||
width: 7%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@media(max-width: 750px) {
|
||||
.speaker-stats-item__name_expressions_on {
|
||||
width: 25%;
|
||||
}
|
||||
.speaker-stats-item__time_expressions_on {
|
||||
width: 30%;
|
||||
}
|
||||
.speaker-stats-item__expression {
|
||||
width: 10%;
|
||||
}
|
||||
}
|
||||
|
||||
.speaker-stats-item__name,
|
||||
.speaker-stats-item__time {
|
||||
.speaker-stats-item__time,
|
||||
.speaker-stats-item__name_expressions_on,
|
||||
.speaker-stats-item__time_expressions_on,
|
||||
.speaker-stats-item__expression {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
.prejoin-preview {
|
||||
&-dropdown-btns {
|
||||
padding: 8px 0;
|
||||
width: calc(100% - 48px);
|
||||
}
|
||||
|
||||
&-dropdown-btn {
|
||||
@@ -59,8 +58,6 @@
|
||||
background: #fff;
|
||||
padding: 0;
|
||||
position: absolute !important;
|
||||
top: 48px !important;
|
||||
transform: none !important;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +138,6 @@
|
||||
|
||||
.toolbox-content-items {
|
||||
background: transparent;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
display: flex;
|
||||
justify-content: space-evenly;
|
||||
@@ -171,6 +170,19 @@
|
||||
}
|
||||
}
|
||||
|
||||
// mobile phone landscape
|
||||
@media (max-height: 420px) {
|
||||
flex-direction: row;
|
||||
|
||||
div.content {
|
||||
padding: 16px 16px 0 16px;
|
||||
}
|
||||
|
||||
.con-status {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 400px) {
|
||||
.content {
|
||||
padding: 16px;
|
||||
|
||||
2
debian/jitsi-meet-prosody.postinst
vendored
2
debian/jitsi-meet-prosody.postinst
vendored
@@ -130,6 +130,8 @@ case "$1" in
|
||||
echo -e " storage = \"memory\"" >> $PROSODY_HOST_CONFIG
|
||||
echo -e " modules_enabled = { \"ping\"; }" >> $PROSODY_HOST_CONFIG
|
||||
echo -e " admins = { \"$JICOFO_AUTH_USER@$JICOFO_AUTH_DOMAIN\", \"jvb@$JICOFO_AUTH_DOMAIN\" }" >> $PROSODY_HOST_CONFIG
|
||||
echo -e " muc_room_locking = false" >> $PROSODY_HOST_CONFIG
|
||||
echo -e " muc_room_default_public_jids = true" >> $PROSODY_HOST_CONFIG
|
||||
fi
|
||||
|
||||
# Convert the old focus component config to the new one.
|
||||
|
||||
2
debian/jitsi-meet-tokens.postinst
vendored
2
debian/jitsi-meet-tokens.postinst
vendored
@@ -61,7 +61,7 @@ case "$1" in
|
||||
sed -i '/^\s*--\s*"token_verification"/ s/--\s*//' $PROSODY_HOST_CONFIG
|
||||
|
||||
# Install luajwt
|
||||
if ! luarocks install luajwtjitsi; then
|
||||
if ! luarocks install luajwtjitsi 2.0-0; then
|
||||
echo "Failed to install luajwtjitsi - try installing it manually"
|
||||
fi
|
||||
|
||||
|
||||
@@ -52,10 +52,12 @@ VirtualHost "jitmeet.example.com"
|
||||
"external_services";
|
||||
"conference_duration";
|
||||
"muc_lobby_rooms";
|
||||
"muc_breakout_rooms";
|
||||
"av_moderation";
|
||||
}
|
||||
c2s_require_encryption = false
|
||||
lobby_muc = "lobby.jitmeet.example.com"
|
||||
breakout_rooms_muc = "breakout.jitmeet.example.com"
|
||||
main_muc = "conference.jitmeet.example.com"
|
||||
-- muc_lobby_whitelist = { "recorder.jitmeet.example.com" } -- Here we can whitelist jibri to enter lobby enabled rooms
|
||||
|
||||
@@ -73,6 +75,18 @@ Component "conference.jitmeet.example.com" "muc"
|
||||
muc_room_locking = false
|
||||
muc_room_default_public_jids = true
|
||||
|
||||
Component "breakout.jitmeet.example.com" "muc"
|
||||
restrict_room_creation = true
|
||||
storage = "memory"
|
||||
modules_enabled = {
|
||||
"muc_meeting_id";
|
||||
"muc_domain_mapper";
|
||||
--"token_verification";
|
||||
}
|
||||
admins = { "focusUser@auth.jitmeet.example.com" }
|
||||
muc_room_locking = false
|
||||
muc_room_default_public_jids = true
|
||||
|
||||
-- internal muc component
|
||||
Component "internal.auth.jitmeet.example.com" "muc"
|
||||
storage = "memory"
|
||||
|
||||
102
flow-typed/npm/uuid_v3.x.x.js
vendored
102
flow-typed/npm/uuid_v3.x.x.js
vendored
@@ -1,102 +0,0 @@
|
||||
// flow-typed signature: 609c1622fc97de96d59519934aa5ce87
|
||||
// flow-typed version: c6154227d1/uuid_v3.x.x/flow_>=v0.32.x <=v0.103.x
|
||||
|
||||
declare module "uuid" {
|
||||
declare class uuid {
|
||||
static (
|
||||
options?: {|
|
||||
random?: number[],
|
||||
rng?: () => number[] | Buffer
|
||||
|},
|
||||
buffer?: number[] | Buffer,
|
||||
offset?: number
|
||||
): string,
|
||||
|
||||
static v1(
|
||||
options?: {|
|
||||
node?: number[],
|
||||
clockseq?: number,
|
||||
msecs?: number | Date,
|
||||
nsecs?: number
|
||||
|},
|
||||
buffer?: number[] | Buffer,
|
||||
offset?: number
|
||||
): string,
|
||||
|
||||
static v4(
|
||||
options?: {|
|
||||
random?: number[],
|
||||
rng?: () => number[] | Buffer
|
||||
|},
|
||||
buffer?: number[] | Buffer,
|
||||
offset?: number
|
||||
): string
|
||||
}
|
||||
declare module.exports: Class<uuid>;
|
||||
}
|
||||
|
||||
declare module "uuid/v1" {
|
||||
declare class v1 {
|
||||
static (
|
||||
options?: {|
|
||||
node?: number[],
|
||||
clockseq?: number,
|
||||
msecs?: number | Date,
|
||||
nsecs?: number
|
||||
|},
|
||||
buffer?: number[] | Buffer,
|
||||
offset?: number
|
||||
): string
|
||||
}
|
||||
|
||||
declare module.exports: Class<v1>;
|
||||
}
|
||||
|
||||
declare module "uuid/v3" {
|
||||
declare class v3 {
|
||||
static (
|
||||
name?: string | number[],
|
||||
namespace?: string | number[],
|
||||
buffer?: number[] | Buffer,
|
||||
offset?: number
|
||||
): string,
|
||||
|
||||
static name: string,
|
||||
static DNS: string,
|
||||
static URL: string
|
||||
}
|
||||
|
||||
declare module.exports: Class<v3>;
|
||||
}
|
||||
|
||||
declare module "uuid/v4" {
|
||||
declare class v4 {
|
||||
static (
|
||||
options?: {|
|
||||
random?: number[],
|
||||
rng?: () => number[] | Buffer
|
||||
|},
|
||||
buffer?: number[] | Buffer,
|
||||
offset?: number
|
||||
): string
|
||||
}
|
||||
|
||||
declare module.exports: Class<v4>;
|
||||
}
|
||||
|
||||
declare module "uuid/v5" {
|
||||
declare class v5 {
|
||||
static (
|
||||
name?: string | number[],
|
||||
namespace?: string | number[],
|
||||
buffer?: number[] | Buffer,
|
||||
offset?: number
|
||||
): string,
|
||||
|
||||
static name: string,
|
||||
static DNS: string,
|
||||
static URL: string
|
||||
}
|
||||
|
||||
declare module.exports: Class<v5>;
|
||||
}
|
||||
71
flow-typed/npm/uuid_v8.x.x.js
vendored
Normal file
71
flow-typed/npm/uuid_v8.x.x.js
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
declare module 'uuid' {
|
||||
// v1 (Timestamp)
|
||||
declare type V1Options = {|
|
||||
node?: $ReadOnlyArray<number>,
|
||||
clockseq?: number,
|
||||
msecs?: number,
|
||||
nsecs?: number,
|
||||
random?: $ReadOnlyArray<number>,
|
||||
rng?: () => $ReadOnlyArray<number>,
|
||||
|};
|
||||
|
||||
declare export function v1(options?: V1Options): string;
|
||||
|
||||
declare export function v1(
|
||||
options: V1Options | null,
|
||||
buffer: Array<number>,
|
||||
offset?: number
|
||||
): Array<number>;
|
||||
|
||||
// v3 (Namespace)
|
||||
declare function v3Impl(
|
||||
name: string | $ReadOnlyArray<number>,
|
||||
namespace: string | $ReadOnlyArray<number>
|
||||
): string;
|
||||
|
||||
declare function v3Impl(
|
||||
name: string | $ReadOnlyArray<number>,
|
||||
namespace: string | $ReadOnlyArray<number>,
|
||||
buffer: Array<number>,
|
||||
offset?: number
|
||||
): Array<number>;
|
||||
|
||||
declare export var v3: {|
|
||||
[[call]]: typeof v3Impl,
|
||||
DNS: string,
|
||||
URL: string,
|
||||
|};
|
||||
|
||||
// v4 (Random)
|
||||
declare type V4Options = {|
|
||||
random?: $ReadOnlyArray<number>,
|
||||
rng?: () => $ReadOnlyArray<number>,
|
||||
|};
|
||||
|
||||
declare export function v4(options?: V4Options): string;
|
||||
|
||||
declare export function v4(
|
||||
options: V4Options | null,
|
||||
buffer: Array<number>,
|
||||
offset?: number
|
||||
): Array<number>;
|
||||
|
||||
// v5 (Namespace)
|
||||
declare function v5Impl(
|
||||
name: string | $ReadOnlyArray<number>,
|
||||
namespace: string | $ReadOnlyArray<number>
|
||||
): string;
|
||||
|
||||
declare function v5Impl(
|
||||
name: string | $ReadOnlyArray<number>,
|
||||
namespace: string | $ReadOnlyArray<number>,
|
||||
buffer: Array<number>,
|
||||
offset?: number
|
||||
): Array<number>;
|
||||
|
||||
declare export var v5: {|
|
||||
[[call]]: typeof v5Impl,
|
||||
DNS: string,
|
||||
URL: string,
|
||||
|};
|
||||
}
|
||||
@@ -57,6 +57,7 @@ target 'JitsiMeetSDK' do
|
||||
pod 'amplitude-react-native', :path => '../node_modules/@amplitude/react-native'
|
||||
pod 'react-native-background-timer', :path => '../node_modules/react-native-background-timer'
|
||||
pod 'react-native-calendar-events', :path => '../node_modules/react-native-calendar-events'
|
||||
pod 'react-native-get-random-values', :path => '../node_modules/react-native-get-random-values'
|
||||
pod 'react-native-keep-awake', :path => '../node_modules/react-native-keep-awake'
|
||||
pod 'react-native-netinfo', :path => '../node_modules/@react-native-community/netinfo'
|
||||
pod 'react-native-performance', :path => '../node_modules/react-native-performance/ios'
|
||||
|
||||
@@ -284,6 +284,8 @@ PODS:
|
||||
- React
|
||||
- react-native-calendar-events (2.0.0):
|
||||
- React
|
||||
- react-native-get-random-values (1.7.0):
|
||||
- React-Core
|
||||
- react-native-keep-awake (4.0.0):
|
||||
- React
|
||||
- react-native-netinfo (4.1.5):
|
||||
@@ -412,6 +414,7 @@ DEPENDENCIES:
|
||||
- React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`)
|
||||
- react-native-background-timer (from `../node_modules/react-native-background-timer`)
|
||||
- react-native-calendar-events (from `../node_modules/react-native-calendar-events`)
|
||||
- react-native-get-random-values (from `../node_modules/react-native-get-random-values`)
|
||||
- react-native-keep-awake (from `../node_modules/react-native-keep-awake`)
|
||||
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
|
||||
- react-native-performance (from `../node_modules/react-native-performance/ios`)
|
||||
@@ -502,6 +505,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-background-timer"
|
||||
react-native-calendar-events:
|
||||
:path: "../node_modules/react-native-calendar-events"
|
||||
react-native-get-random-values:
|
||||
:path: "../node_modules/react-native-get-random-values"
|
||||
react-native-keep-awake:
|
||||
:path: "../node_modules/react-native-keep-awake"
|
||||
react-native-netinfo:
|
||||
@@ -603,6 +608,7 @@ SPEC CHECKSUMS:
|
||||
React-jsinspector: 92ceee6c66dc19886289b52436ade7e020b89602
|
||||
react-native-background-timer: 029c606b3fd6dd476b153e177c518b6ade4c169f
|
||||
react-native-calendar-events: 82dc6c915653a1a8a266e43fdbbfdb3b1022ca99
|
||||
react-native-get-random-values: 237bffb1c7e05fb142092681531810a29ba53015
|
||||
react-native-keep-awake: afad8a51dfef9fe9655a6344771be32c8596d774
|
||||
react-native-netinfo: 0e563248a4b9a99c33ec29bd03c2d50767db22a6
|
||||
react-native-performance: 6bd6cfac80594775fb782405fceaaf206becf53b
|
||||
@@ -635,6 +641,6 @@ SPEC CHECKSUMS:
|
||||
RNWatch: a5320c959c75e72845c07985f3e935e58998f1d3
|
||||
Yoga: 96b469c5e81ff51b917b92e8c3390642d4ded30c
|
||||
|
||||
PODFILE CHECKSUM: 42be6796ba6ac039dae5c02125677728ecd0df0d
|
||||
PODFILE CHECKSUM: 836d4804218c0608e1326471ec83fe31cfa9c86d
|
||||
|
||||
COCOAPODS: 1.11.2
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
0BD906EA1EC0C00300C8C18E /* JitsiMeet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BD906E81EC0C00300C8C18E /* JitsiMeet.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
4E51B76425E5345E0038575A /* ScheenshareEventEmiter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E51B76225E5345E0038575A /* ScheenshareEventEmiter.h */; };
|
||||
4E51B76525E5345E0038575A /* ScheenshareEventEmiter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E51B76325E5345E0038575A /* ScheenshareEventEmiter.m */; };
|
||||
4ED4FFF32721B9B90074E620 /* JitsiAudioSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ED4FFF12721B9B90074E620 /* JitsiAudioSession.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
4ED4FFF42721B9B90074E620 /* JitsiAudioSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ED4FFF22721B9B90074E620 /* JitsiAudioSession.m */; };
|
||||
6F08DF7D4458EE3CF3F36F6D /* libPods-JitsiMeetSDK.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E4376CA6886DE68FD7A4294B /* libPods-JitsiMeetSDK.a */; };
|
||||
A4A934E9212F3ADB001E9388 /* Dropbox.m in Sources */ = {isa = PBXBuildFile; fileRef = A4A934E8212F3ADB001E9388 /* Dropbox.m */; };
|
||||
C6245F5D2053091D0040BE68 /* image-resize@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C6245F5B2053091D0040BE68 /* image-resize@2x.png */; };
|
||||
@@ -77,6 +79,9 @@
|
||||
0BD906E91EC0C00300C8C18E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
4E51B76225E5345E0038575A /* ScheenshareEventEmiter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScheenshareEventEmiter.h; sourceTree = "<group>"; };
|
||||
4E51B76325E5345E0038575A /* ScheenshareEventEmiter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScheenshareEventEmiter.m; sourceTree = "<group>"; };
|
||||
4ED4FFF12721B9B90074E620 /* JitsiAudioSession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiAudioSession.h; sourceTree = "<group>"; };
|
||||
4ED4FFF22721B9B90074E620 /* JitsiAudioSession.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiAudioSession.m; sourceTree = "<group>"; };
|
||||
4ED4FFF52721BAE10074E620 /* JitsiAudioSession+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiAudioSession+Private.h"; sourceTree = "<group>"; };
|
||||
891FE43DAD30BC8976683100 /* Pods-JitsiMeetSDK.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeetSDK.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeetSDK/Pods-JitsiMeetSDK.release.xcconfig"; sourceTree = "<group>"; };
|
||||
98E09B5C73D9036B4ED252FC /* Pods-JitsiMeet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
9C77CA3CC919B081F1A52982 /* Pods-JitsiMeet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.release.xcconfig"; sourceTree = "<group>"; };
|
||||
@@ -174,6 +179,9 @@
|
||||
DEFE535821FB311F00011A3A /* JitsiMeet+Private.h */,
|
||||
DEA9F283258A5D9900D4CD74 /* JitsiMeetSDK.h */,
|
||||
DEFE535321FB1BF800011A3A /* JitsiMeet.m */,
|
||||
4ED4FFF12721B9B90074E620 /* JitsiAudioSession.h */,
|
||||
4ED4FFF52721BAE10074E620 /* JitsiAudioSession+Private.h */,
|
||||
4ED4FFF22721B9B90074E620 /* JitsiAudioSession.m */,
|
||||
DEAD3224220C497000E93636 /* JitsiMeetConferenceOptions.h */,
|
||||
DEAD3228220C734300E93636 /* JitsiMeetConferenceOptions+Private.h */,
|
||||
DEAD3225220C497000E93636 /* JitsiMeetConferenceOptions.m */,
|
||||
@@ -277,6 +285,7 @@
|
||||
4E51B76425E5345E0038575A /* ScheenshareEventEmiter.h in Headers */,
|
||||
DE65AACC2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h in Headers */,
|
||||
0B412F221EDEF6EA00B1A0A6 /* JitsiMeetViewDelegate.h in Headers */,
|
||||
4ED4FFF32721B9B90074E620 /* JitsiAudioSession.h in Headers */,
|
||||
0BD906EA1EC0C00300C8C18E /* JitsiMeet.h in Headers */,
|
||||
DE81A2D42316AC4D00AE1940 /* JitsiMeetLogger.h in Headers */,
|
||||
DE65AACA2317FFCD00290BEC /* LogUtils.h in Headers */,
|
||||
@@ -438,6 +447,7 @@
|
||||
files = (
|
||||
0BB9AD7B1F5EC8F4001C08DB /* CallKit.m in Sources */,
|
||||
DE81A2DF2317ED5400AE1940 /* JitsiMeetBaseLogHandler.m in Sources */,
|
||||
4ED4FFF42721B9B90074E620 /* JitsiAudioSession.m in Sources */,
|
||||
0BB9AD7D1F60356D001C08DB /* AppInfo.m in Sources */,
|
||||
DE81A2D92316AC7600AE1940 /* LogBridge.m in Sources */,
|
||||
DEAFA779229EAD520033A7FA /* RNRootView.m in Sources */,
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#import <React/RCTLog.h>
|
||||
#import <WebRTC/WebRTC.h>
|
||||
|
||||
#import "JitsiAudioSession+Private.h"
|
||||
#import "LogUtils.h"
|
||||
|
||||
|
||||
@@ -113,7 +114,7 @@ RCT_EXPORT_MODULE();
|
||||
isSpeakerOn = NO;
|
||||
isEarpieceOn = NO;
|
||||
|
||||
RTCAudioSession *session = [RTCAudioSession sharedInstance];
|
||||
RTCAudioSession *session = JitsiAudioSession.rtcAudioSession;
|
||||
[session addDelegate:self];
|
||||
}
|
||||
|
||||
@@ -127,7 +128,7 @@ RCT_EXPORT_MODULE();
|
||||
|
||||
- (BOOL)setConfigWithoutLock:(RTCAudioSessionConfiguration *)config
|
||||
error:(NSError * _Nullable *)outError {
|
||||
RTCAudioSession *session = [RTCAudioSession sharedInstance];
|
||||
RTCAudioSession *session = JitsiAudioSession.rtcAudioSession;
|
||||
|
||||
return [session setConfiguration:config error:outError];
|
||||
}
|
||||
@@ -135,7 +136,7 @@ RCT_EXPORT_MODULE();
|
||||
- (BOOL)setConfig:(RTCAudioSessionConfiguration *)config
|
||||
error:(NSError * _Nullable *)outError {
|
||||
|
||||
RTCAudioSession *session = [RTCAudioSession sharedInstance];
|
||||
RTCAudioSession *session = JitsiAudioSession.rtcAudioSession;
|
||||
[session lockForConfiguration];
|
||||
BOOL success = [self setConfigWithoutLock:config error:outError];
|
||||
[session unlockForConfiguration];
|
||||
@@ -178,7 +179,7 @@ RCT_EXPORT_METHOD(setAudioDevice:(NSString *)device
|
||||
reject:(RCTPromiseRejectBlock)reject) {
|
||||
DDLogInfo(@"[AudioMode] Selected device: %@", device);
|
||||
|
||||
RTCAudioSession *session = [RTCAudioSession sharedInstance];
|
||||
RTCAudioSession *session = JitsiAudioSession.rtcAudioSession;
|
||||
[session lockForConfiguration];
|
||||
BOOL success;
|
||||
NSError *error = nil;
|
||||
@@ -273,7 +274,7 @@ RCT_EXPORT_METHOD(updateDeviceList) {
|
||||
RTCAudioSessionConfiguration *config = [self configForMode:self->activeMode];
|
||||
[self setConfig:config error:nil];
|
||||
if (self->forceSpeaker && !self->isSpeakerOn) {
|
||||
RTCAudioSession *session = [RTCAudioSession sharedInstance];
|
||||
RTCAudioSession *session = JitsiAudioSession.rtcAudioSession;
|
||||
[session lockForConfiguration];
|
||||
[session overrideOutputAudioPort:AVAudioSessionPortOverrideSpeaker error:nil];
|
||||
[session unlockForConfiguration];
|
||||
|
||||
24
ios/sdk/src/JitsiAudioSession+Private.h
Normal file
24
ios/sdk/src/JitsiAudioSession+Private.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright @ 2017-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "JitsiAudioSession.h"
|
||||
#import <WebRTC/WebRTC.h>
|
||||
|
||||
@interface JitsiAudioSession (Private)
|
||||
|
||||
+ (RTCAudioSession *)rtcAudioSession;
|
||||
|
||||
@end
|
||||
26
ios/sdk/src/JitsiAudioSession.h
Normal file
26
ios/sdk/src/JitsiAudioSession.h
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright @ 2017-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@class AVAudioSession;
|
||||
|
||||
@interface JitsiAudioSession : NSObject
|
||||
|
||||
+ (void)activateWithAudioSession:(AVAudioSession *)session;
|
||||
+ (void)deactivateWithAudioSession:(AVAudioSession *)session;
|
||||
|
||||
@end
|
||||
34
ios/sdk/src/JitsiAudioSession.m
Normal file
34
ios/sdk/src/JitsiAudioSession.m
Normal file
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright @ 2017-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "JitsiAudioSession.h"
|
||||
#import "JitsiAudioSession+Private.h"
|
||||
|
||||
@implementation JitsiAudioSession
|
||||
|
||||
+ (RTCAudioSession *)rtcAudioSession {
|
||||
return [RTCAudioSession sharedInstance];
|
||||
}
|
||||
|
||||
+ (void)activateWithAudioSession:(AVAudioSession *)session {
|
||||
[self.rtcAudioSession audioSessionDidActivate:session];
|
||||
}
|
||||
|
||||
+ (void)deactivateWithAudioSession:(AVAudioSession *)session {
|
||||
[self.rtcAudioSession audioSessionDidDeactivate:session];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -20,4 +20,5 @@
|
||||
#import <JitsiMeetSDK/JitsiMeetConferenceOptions.h>
|
||||
#import <JitsiMeetSDK/JitsiMeetLogger.h>
|
||||
#import <JitsiMeetSDK/JitsiMeetBaseLogHandler.h>
|
||||
#import <JitsiMeetSDK/JitsiAudioSession.h>
|
||||
#import <JitsiMeetSDK/InfoPlistUtil.h>
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#import <JitsiMeetSDK/JitsiMeetSDK-Swift.h>
|
||||
|
||||
#import "../LogUtils.h"
|
||||
#import "JitsiAudioSession.h"
|
||||
|
||||
|
||||
// The events emitted/supported by RNCallKit:
|
||||
@@ -319,13 +320,13 @@ RCT_EXPORT_METHOD(updateCall:(NSString *)callUUID
|
||||
- (void) providerDidActivateAudioSessionWithSession:(AVAudioSession *)session {
|
||||
DDLogInfo(@"[RNCallKit][CXProviderDelegate][provider:didActivateAudioSession:]");
|
||||
|
||||
[[RTCAudioSession sharedInstance] audioSessionDidActivate:session];
|
||||
[JitsiAudioSession activateWithAudioSession:session];
|
||||
}
|
||||
|
||||
- (void) providerDidDeactivateAudioSessionWithSession:(AVAudioSession *)session {
|
||||
DDLogInfo(@"[RNCallKit][CXProviderDelegate][provider:didDeactivateAudioSession:]");
|
||||
|
||||
[[RTCAudioSession sharedInstance] audioSessionDidDeactivate:session];
|
||||
[JitsiAudioSession deactivateWithAudioSession:session];
|
||||
}
|
||||
|
||||
- (void) providerTimedOutPerformingActionWithAction:(CXAction *)action {
|
||||
|
||||
@@ -236,7 +236,7 @@
|
||||
"remoteControlDeniedMessage": "{{user}} ha rebutjat la petició de control remot!",
|
||||
"remoteControlErrorMessage": "S'ha produït un error en intentar sol·licitar a {{user}} permisos de control remot!",
|
||||
"remoteControlRequestMessage": "Voleu permetre a {{user}} de controlar remotament el vostre escriptori?",
|
||||
"remoteControlShareScreenWarning": "Tingueu present que si pitgeu \"Permet\" compartireu la vostra pantalla!",
|
||||
"remoteControlShareScreenWarning": "Tingueu present que si pitgeu \"Permet\"compartireu la vostra pantalla!",
|
||||
"remoteControlStopMessage": "La sessió de control remot ha finalitzat!",
|
||||
"remoteControlTitle": "Control d'escriptori remot",
|
||||
"Remove": "Suprimeix",
|
||||
@@ -577,7 +577,7 @@
|
||||
},
|
||||
"startupoverlay": {
|
||||
"title": "{{app}} requereix usar el micròfon i la càmera.",
|
||||
"policyText": " "
|
||||
"policyText": ""
|
||||
},
|
||||
"suspendedoverlay": {
|
||||
"rejoinKeyTitle": "Torna a entrar",
|
||||
@@ -782,5 +782,42 @@
|
||||
},
|
||||
"helpView": {
|
||||
"header": "Centre d'ajuda"
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Acceptar",
|
||||
"admitAll": "Acceptar Tots",
|
||||
"knockingParticipantList": "Participants en espera",
|
||||
"allow": "Permetre",
|
||||
"backToKnockModeButton": "Sol·licitar entrada",
|
||||
"dialogTitle": "Sala d’espera",
|
||||
"disableDialogContent": "La sala d’espera està habilitada actualment. Aquesta funció garanteix que els participants no desitjats no puguin unir-se a la seva reunió. Vols deshabilitar-ho?",
|
||||
"disableDialogSubmit": "Deshabilitat",
|
||||
"emailField": "Introdueix el teu email",
|
||||
"enableDialogPasswordField": "Estableix la contrasenya (opcional)",
|
||||
"enableDialogSubmit": "Habilitat",
|
||||
"enableDialogText": "La sala d’espera li permet protegir la seva reunió en permetre que les persones només entrin després d’una aprovació formal per part d’un moderador.",
|
||||
"enterPasswordButton": "Introduir la contrasenya de la reunió",
|
||||
"enterPasswordTitle": "Introdueixi la contrasenya per a unir-se a la reunió",
|
||||
"errorMissingPassword": "Si us plau, Introdueixi la contrasenya de la reunió",
|
||||
"invalidPassword": "Contrasenya errònia",
|
||||
"joiningMessage": "T’uniràs a la reunió tan aviat com algú accepti la teva sol·licitud",
|
||||
"joinWithPasswordMessage": "Intentant unir-se amb contrasenya, esperi ...",
|
||||
"joinRejectedMessage": "Un moderador va rebutjar la seva sol·licitud per a unir-se.",
|
||||
"joinTitle": "Unir-se a la reunió",
|
||||
"joiningTitle": "Sol·licitar unir-se a la reunió...",
|
||||
"joiningWithPasswordTitle": "Connectant-se a la reunió amb contrasenya...",
|
||||
"knockButton": "Sol·licitar entrada a la reunió",
|
||||
"knockTitle": "Algú vol unir-se a la reunió",
|
||||
"nameField": "Introdueix el teu nom",
|
||||
"notificationLobbyAccessDenied": "{{originParticipantName}} ha rebutjat l’entrada a la reunió de {{targetParticipantName}}",
|
||||
"notificationLobbyAccessGranted": "{{originParticipantName}} ha acceptat l’entrada a la reunió de {{targetParticipantName}}",
|
||||
"notificationLobbyDisabled": "La sala d’espera ha estat deshabilitada per {{originParticipantName}}",
|
||||
"notificationLobbyEnabled": "La sala d’espera ha estat habilitada per {{originParticipantName}}",
|
||||
"notificationTitle": "Sala d’espera",
|
||||
"passwordField": "Introduir la contrasenya de la reunió",
|
||||
"passwordJoinButton": "Unir-se",
|
||||
"reject": "Rebutjar",
|
||||
"rejectAll": "Rebutjar Tots",
|
||||
"toggleLabel": "Habilitar sala d’espera"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -213,7 +213,9 @@
|
||||
"done": "Fertig",
|
||||
"e2eeDescription": "Ende-zu-Ende-Verschlüsselung ist derzeit noch EXPERIMENTELL. Bitte beachten Sie, dass das Aktivieren der Ende-zu-Ende-Verschlüsselung diverse serverseitige Funktionen deaktiviert: Aufnahmen, Livestreaming und Telefoneinwahl. Bitte beachten Sie außerdem, dass der Konferenz dann nur noch mit Browsern beigetreten werden kann, die Insertable Streams unterstützen.",
|
||||
"e2eeLabel": "Ende-zu-Ende-Verschlüsselung aktivieren",
|
||||
"e2eeDisabledDueToMaxModeDescription": "Ende-zu-Ende-Verschlüsselung kann aufgrund der großen Zahl an Anwesenden nicht aktiviert werden.",
|
||||
"e2eeWarning": "WARNUNG: Nicht alle Personen dieser Konferenz scheinen Ende-zu-Ende-Verschlüsselung zu unterstützen. Wenn Sie diese aktivieren, können die entsprechenden Personen nichts mehr sehen oder hören.",
|
||||
"e2eeWillDisableDueToMaxModeDescription": "WARNUNG: Ende-zu-Ende-Verschlüsselung wird automatisch deaktiviert, wenn weitere Anwesende an der Konferenz teilnehmen.",
|
||||
"enterDisplayName": "Bitte geben Sie hier Ihren Namen ein",
|
||||
"embedMeeting": "Besprechung einbetten",
|
||||
"error": "Fehler",
|
||||
@@ -333,6 +335,7 @@
|
||||
"shareScreenWarningH1": "Wenn Sie Ihren Bildschirm freigeben wollen:",
|
||||
"shareScreenWarningD1": "müssen Sie Ihre Audiofreigabe stoppen, bevor Sie ihren Bildschirm freigeben.",
|
||||
"shareScreenWarningD2": "müssen Sie Ihre Audiofreigabe stoppen und dann die Bildschirmfreigabe mit der Option \"Audio freigeben\" starten.",
|
||||
"sharedVideoLinkPlaceholder": "YouTube-URL oder direkte Video-URL",
|
||||
"stopLiveStreaming": "Livestream stoppen",
|
||||
"stopRecording": "Aufnahme stoppen",
|
||||
"stopRecordingWarning": "Sind Sie sicher, dass Sie die Aufnahme stoppen möchten?",
|
||||
@@ -498,6 +501,7 @@
|
||||
"expandedPending": "Livestream wird gestartet …",
|
||||
"failedToStart": "Livestream konnte nicht gestartet werden",
|
||||
"getStreamKeyManually": "Wir waren nicht in der Lage, Livestreams abzurufen. Versuchen Sie, Ihren Livestream-Schlüssel von YouTube zu erhalten.",
|
||||
"inProgress": "Livestreaming gestartet",
|
||||
"invalidStreamKey": "Der Livestream-Schlüssel ist u. U. falsch.",
|
||||
"off": "Livestream gestoppt",
|
||||
"offBy": "{{name}} stoppte den Livestream",
|
||||
@@ -505,6 +509,7 @@
|
||||
"onBy": "{{name}} startete den Livestream",
|
||||
"pending": "Livestream wird gestartet …",
|
||||
"serviceName": "Livestreaming-Dienst",
|
||||
"sessionAlreadyActive": "Diese Konferenz wird bereits als Livestream übertragen.",
|
||||
"signedInAs": "Sie sind derzeit angemeldet als:",
|
||||
"signIn": "Mit Google anmelden",
|
||||
"signInCTA": "Anmelden oder den Streamschlüssel des YouTube-Livestreams eingeben.",
|
||||
@@ -548,13 +553,14 @@
|
||||
"lockRoomPasswordUppercase": "Passwort",
|
||||
"me": "ich",
|
||||
"notify": {
|
||||
"allowAction": "Erlauben",
|
||||
"allowedUnmute": "Sie können die Stummschaltung aufheben, Ihre Kamera einschalten oder Ihren Bildschirm teilen.",
|
||||
"connectedOneMember": "{{name}} nimmt am Meeting teil",
|
||||
"connectedThreePlusMembers": "{{name}} und {{count}} andere Personen nehmen am Meeting teil",
|
||||
"connectedTwoMembers": "{{first}} und {{second}} nehmen am Meeting teil",
|
||||
"disconnected": "getrennt",
|
||||
"focus": "Konferenzleitung",
|
||||
"focusFail": "{{component}} ist im Moment nicht verfügbar – wiederholen in {{ms}} Sekunden",
|
||||
"grantedTo": "Moderationsrechte an {{to}} vergeben!",
|
||||
"hostAskedUnmute": "Die Moderation bittet Sie, das Mikrofon zu aktivieren",
|
||||
"invitedOneMember": "{{name}} wurde eingeladen",
|
||||
"invitedThreePlusMembers": "{{name}} und {{count}} andere wurden eingeladen",
|
||||
@@ -598,7 +604,6 @@
|
||||
"moderationStoppedTitle": "Moderation gestoppt",
|
||||
"moderationToggleDescription": "von {{participantDisplayName}}",
|
||||
"raiseHandAction": "Melden",
|
||||
"groupTitle": "Benachrichtigungen",
|
||||
"reactionSounds": "Interaktionstöne deaktivieren",
|
||||
"groupTitle": "Benachrichtigungen"
|
||||
},
|
||||
@@ -612,10 +617,12 @@
|
||||
},
|
||||
"actions": {
|
||||
"allow": "Anwesenden erlauben:",
|
||||
"allowVideo": "Kamera einschalten",
|
||||
"audioModeration": "Für sich selbst die Stummschaltung aufzuheben",
|
||||
"blockEveryoneMicCamera": "Kamera und Mikrofon von allen sperren",
|
||||
"invite": "Person einladen",
|
||||
"askUnmute": "Anfragen, Stummschaltung aufzuheben",
|
||||
"moreModerationActions": "Weitere Moderationsoptionen",
|
||||
"mute": "Stummschalten",
|
||||
"muteAll": "Alle stummschalten",
|
||||
"muteEveryoneElse": "Alle anderen stummschalten",
|
||||
@@ -623,11 +630,13 @@
|
||||
"stopVideo": "Kamera ausschalten",
|
||||
"unblockEveryoneMicCamera": "Kamera und Mikrofon von allen entsperren",
|
||||
"videoModeration": "Kamera einschalten"
|
||||
}
|
||||
},
|
||||
"search": "Suche Anwesende"
|
||||
},
|
||||
"passwordSetRemotely": "von einer anderen Person gesetzt",
|
||||
"passwordDigitsOnly": "Bis zu {{number}} Ziffern",
|
||||
"polls": {
|
||||
"by": "Von {{ name }}",
|
||||
"create": {
|
||||
"addOption": "Antwort hinzufügen",
|
||||
"answerPlaceholder": "Antwort {{index}}",
|
||||
@@ -695,6 +704,7 @@
|
||||
"errorDialOutFailed": "Anruf fehlgeschlagen. Anruf fehlgeschlagen",
|
||||
"errorDialOutStatus": "Fehler beim Abrufen des Anrufstatus",
|
||||
"errorMissingName": "Bitte geben Sie Ihren Namen ein, um der Konferenz beizutreten.",
|
||||
"errorNoPermissions": "Sie müssen den Zugriff auf Mikrofon und Kamera erlauben",
|
||||
"errorStatusCode": "Anruf fehlgeschlagen. Statuscode: {{status}}",
|
||||
"errorValidation": "Nummerverifikation fehlgeschlagen",
|
||||
"iWantToDialIn": "Ich möchte mich einwählen",
|
||||
@@ -752,6 +762,7 @@
|
||||
"expandedPending": "Aufzeichnung wird gestartet…",
|
||||
"failedToStart": "Die Aufnahme konnte nicht gestartet werden",
|
||||
"fileSharingdescription": "Aufzeichnung mit den Personen der Konferenz teilen",
|
||||
"inProgress": "Aufzeichnung gestartet",
|
||||
"linkGenerated": "Link zur Aufzeichnung wurde generiert.",
|
||||
"live": "LIVE",
|
||||
"loggedIn": "Als {{userName}} angemeldet",
|
||||
@@ -764,6 +775,7 @@
|
||||
"serviceDescription": "Ihre Aufzeichnung wird vom Aufzeichnungsdienst gespeichert",
|
||||
"serviceDescriptionCloud": "Cloud-Aufzeichnung",
|
||||
"serviceName": "Aufnahmedienst",
|
||||
"sessionAlreadyActive": "Diese Konferenz wird bereits aufgezeichnet.",
|
||||
"signIn": "Anmelden",
|
||||
"signOut": "Abmelden",
|
||||
"unavailable": "Oh! Der {{serviceName}} ist aktuell nicht verfügbar. Wir arbeiten an der Behebung des Problems. Bitte versuchen Sie es später noch einmal.",
|
||||
@@ -850,7 +862,14 @@
|
||||
"name": "Name",
|
||||
"seconds": "{{count}} Sek.",
|
||||
"speakerStats": "Sprechstatistik",
|
||||
"speakerTime": "Sprechzeit"
|
||||
"speakerTime": "Sprechzeit",
|
||||
"happy": "Fröhlich",
|
||||
"neutral": "Neutral",
|
||||
"sad": "Traurig",
|
||||
"surprised": "Überrascht",
|
||||
"angry": "Sauer",
|
||||
"fearful": "Ängstlich",
|
||||
"disgusted": "Angeekelt"
|
||||
},
|
||||
"startupoverlay": {
|
||||
"policyText": " ",
|
||||
@@ -898,7 +917,7 @@
|
||||
"pip": "Bild-in-Bild-Modus ein-/ausschalten",
|
||||
"privateMessage": "Private Nachricht senden",
|
||||
"profile": "Profil bearbeiten",
|
||||
"raiseHand": "Hand erheben / senken",
|
||||
"raiseHand": "Hand heben",
|
||||
"reactionsMenu": "Interaktionsmenü öffnen / schließen",
|
||||
"recording": "Aufzeichnung ein-/ausschalten",
|
||||
"remoteMute": "Personen stummschalten",
|
||||
@@ -935,7 +954,7 @@
|
||||
"chat": "Chat öffnen / schließen",
|
||||
"clap": "Klatschen",
|
||||
"closeChat": "Chat schließen",
|
||||
"closeReactionsMenu": "Interationsmenü schließen",
|
||||
"closeReactionsMenu": "Interaktionsmenü schließen",
|
||||
"disableReactionSounds": "Sie können die Interaktionstöne für diese Konferenz deaktivieren",
|
||||
"documentClose": "Geteiltes Dokument schließen",
|
||||
"documentOpen": "Geteiltes Dokument öffnen",
|
||||
@@ -970,12 +989,12 @@
|
||||
"noisyAudioInputTitle": "Ihr Mikrofon scheint lärmintensiv zu sein!",
|
||||
"noisyAudioInputDesc": "Es klingt, als ob Ihr Mikrofon Störgeräusche verursacht. Bitte überlegen Sie, ob Sie das Gerät stummschalten oder austauschen wollen.",
|
||||
"openChat": "Chat öffnen",
|
||||
"openReactionsMenu": "Interationsmenü öffnen",
|
||||
"openReactionsMenu": "Interaktionsmenü öffnen",
|
||||
"participants": "Anwesende",
|
||||
"pip": "Bild-in-Bild-Modus einschalten",
|
||||
"privateMessage": "Private Nachricht senden",
|
||||
"profile": "Profil bearbeiten",
|
||||
"raiseHand": "Hand erheben / senken",
|
||||
"raiseHand": "Hand heben",
|
||||
"raiseYourHand": "Melden",
|
||||
"reactionBoo": "Buhen senden",
|
||||
"reactionClap": "Klatschen senden",
|
||||
@@ -1040,7 +1059,10 @@
|
||||
"pending": "{{displayName}} wurde eingeladen"
|
||||
},
|
||||
"videoStatus": {
|
||||
"adjustFor": "Einstellen für:",
|
||||
"audioOnly": "AUD",
|
||||
"bestPerformance": "Beste Leistung",
|
||||
"highestQuality": "Höchste Qualität",
|
||||
"audioOnlyExpanded": "Sie befinden sich im Modus „Nur Audio“. Dieser Modus benötigt weniger Bandbreite, Sie sehen jedoch nicht die Videos der anderen.",
|
||||
"callQuality": "Videoqualität",
|
||||
"hd": "HD",
|
||||
@@ -1051,6 +1073,7 @@
|
||||
"ld": "LD",
|
||||
"ldTooltip": "Video wird in niedriger Auflösung angezeigt",
|
||||
"lowDefinition": "Niedrige Auflösung",
|
||||
"performanceSettings": "Qualitätseinstellungen",
|
||||
"sd": "SD",
|
||||
"sdTooltip": "Video wird in Standardauflösung angezeigt",
|
||||
"standardDefinition": "Standardauflösung"
|
||||
@@ -1124,6 +1147,12 @@
|
||||
"button": "Andere einladen",
|
||||
"youAreAlone": "Nur Sie sind in dieser Konferenz"
|
||||
},
|
||||
"termsView": {
|
||||
"header": "Nutzungsbedingungen"
|
||||
},
|
||||
"privacyView": {
|
||||
"header": "Datenschutz"
|
||||
},
|
||||
"helpView": {
|
||||
"header": "Hilfecenter"
|
||||
},
|
||||
|
||||
@@ -187,7 +187,7 @@
|
||||
"Back": "Retour",
|
||||
"cameraConstraintFailedError": "Votre caméra ne satisfait pas certaines des contraintes nécessaires.",
|
||||
"cameraNotFoundError": "La caméra n'a pas été trouvée.",
|
||||
"cameraNotSendingData": "Nous sommes incapables d'accéder à votre caméra. Veuillez sélectionner un autre périphérique dans les paramètres ou rafraîchir la page.",
|
||||
"cameraNotSendingData": "Impossible d'accéder à votre caméra. Veuillez sélectionner un autre périphérique dans les paramètres ou rafraîchir la page.",
|
||||
"cameraNotSendingDataTitle": "Impossible d'accéder à votre caméra",
|
||||
"cameraPermissionDeniedError": "Vous n'avez pas autorisé l'utilisation de votre caméra. Vous pouvez toujours participer à la conférence, mais les autres ne pourront pas vous voir. Utilisez le bouton de la caméra dans la barre d'adresse pour résoudre ce problème.",
|
||||
"cameraTimeoutError": "Impossible de démarrer la source vidéo. Délai dépassé!",
|
||||
@@ -211,9 +211,11 @@
|
||||
"dismiss": "Rejeter",
|
||||
"displayNameRequired": "Bonjour ! Quel est votre nom ?",
|
||||
"done": "Terminé",
|
||||
"e2eeDescription": "Le chiffrement de Bout-en-Bout est actuellement EXPERIMENTAL. Veuillez garder à l'esprit que l'activation du chiffrement de Bout-en-Bout désactivera les services fournis côté serveur tels que : l'enregistrement, la diffusion en direct et la participation par téléphone. Gardez également à l'esprit que la réunion ne fonctionnera que pour les personnes qui se joignent à partir de navigateurs prenant en charge les flux insérables.",
|
||||
"e2eeDescription": "Le chiffrement de Bout-en-Bout est actuellement EXPERIMENTAL. Veuillez garder à l'esprit que l'activation du chiffrement de Bout-en-Bout désactivera les services fournis côté serveur tels que : l'enregistrement, la diffusion en direct et la participation par téléphone. Gardez également à l'esprit que la réunion ne fonctionnera que pour les personnes qui se connectent à partir de navigateurs prenant en charge les flux insérables.",
|
||||
"e2eeLabel": "Activer le chiffrement de Bout-en-Bout",
|
||||
"e2eeDisabledDueToMaxModeDescription": "Impossible d'activer le chiffrement de bout en bout en raison du trop grand nombre de participants à la conférence.",
|
||||
"e2eeWarning": "ATTENTION : Tous les participants de cette réunion ne semblent pas prendre en charge le chiffrement de Bout-en-Bout. Si vous activez le chiffrement, ils ne pourront ni vous voir, ni vous entendre.",
|
||||
"e2eeWillDisableDueToMaxModeDescription": "ATTENTION: le chiffrement de bout en bout sera automatiquement arrêté si plus de participants joignent la conférence.",
|
||||
"enterDisplayName": "Merci de saisir votre nom ici",
|
||||
"embedMeeting": "Intégrer la réunion",
|
||||
"error": "Erreur",
|
||||
@@ -221,6 +223,7 @@
|
||||
"grantModeratorDialog": "Êtes-vous sûr de vouloir rendre ce participant modérateur ?",
|
||||
"grantModeratorTitle": "Nommer modérateur",
|
||||
"IamHost": "Je suis l'hôte",
|
||||
"hideShareAudioHelper": "Ne pas montrer ce dialogue à nouveau",
|
||||
"incorrectRoomLockPassword": "Mot de passe incorrect",
|
||||
"incorrectPassword": "Nom d'utilisateur ou mot de passe incorrect",
|
||||
"internalError": "Oups ! Quelque chose s'est mal passée. L'erreur suivante s'est produite : {{error}}",
|
||||
@@ -232,14 +235,14 @@
|
||||
"kickTitle": "Oups ! vous avez été expulsé(e) par {{participantDisplayName}}",
|
||||
"liveStreaming": "Direct",
|
||||
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "Impossible durant l'enregistrement",
|
||||
"liveStreamingDisabledTooltip": "La diffusion en direct est désactivé",
|
||||
"liveStreamingDisabledTooltip": "La diffusion en direct est désactivée",
|
||||
"lockMessage": "Impossible de verrouiller la conférence.",
|
||||
"lockRoom": "Ajouter un $t(lockRoomPassword) à la réunion ",
|
||||
"lockTitle": "Échec du verrouillage",
|
||||
"login": "Connexion",
|
||||
"logoutQuestion": "Voulez-vous vraiment vous déconnecter et arrêter la conférence ?",
|
||||
"logoutTitle": "Déconnexion",
|
||||
"maxUsersLimitReached": "Le nombre maximal de participant est atteint. Le conférence est complète. Merci de contacter l'organisateur de la réunion ou réessayer plus tard !",
|
||||
"maxUsersLimitReached": "Le nombre maximal de participants est atteint. Le conférence est complète. Merci de contacter l'organisateur de la réunion ou réessayer plus tard !",
|
||||
"maxUsersLimitReachedTitle": "Le nombre maximal de participants est atteint",
|
||||
"micConstraintFailedError": "Votre microphone ne satisfait pas certaines des contraintes nécessaires.",
|
||||
"micNotFoundError": "Le microphone n'a pas été détecté.",
|
||||
@@ -253,10 +256,12 @@
|
||||
"muteEveryoneElseDialog": "Une fois leur micro coupé, vous ne pourrez plus le réactiver, mais ils pourront l'activer par eux-mêmes à tout moment.",
|
||||
"muteEveryoneElseTitle": "Couper le micro de tout le monde sauf de {{whom}} ?",
|
||||
"muteEveryoneDialog": "Êtes-vous sûr de vouloir couper les micros de tout le monde ? Vous ne pourrez plus réactiver leur micro, mais ils pourront l'activer par eux-mêmes à tout moment.",
|
||||
"muteEveryoneDialogModerationOn": "Les participants peuvent demander à parler n'importe quand",
|
||||
"muteEveryoneTitle": "Couper le micro de tout le monde ?",
|
||||
"muteEveryoneElsesVideoDialog": "Une fois la caméra coupée, vous ne pourrez plus la rallumer, mais ils peuvent la rallumer à tout moment.",
|
||||
"muteEveryoneElsesVideoTitle": "Couper la vidéo de tout le monde sauf {{whom}}?",
|
||||
"muteEveryonesVideoDialog": "Êtes-vous sûr de vouloir couper la caméra de tout le monde? Vous ne pourrez pas la réactiver, mais ils peuvent la remettre à tout moment.",
|
||||
"muteEveryonesVideoDialogModerationOn": "Les participants peuvent demander à activer leur caméra n'importe quand.",
|
||||
"muteEveryonesVideoDialogOk": "Désactiver",
|
||||
"muteEveryonesVideoTitle": "Couper la caméra de tout le monde?",
|
||||
"muteEveryoneSelf": "vous",
|
||||
@@ -264,16 +269,21 @@
|
||||
"muteParticipantBody": "Vous ne pourrez plus réactiver son micro, mais il pourra l'activer par lui-même à tout moment.",
|
||||
"muteParticipantButton": "Couper le micro",
|
||||
"muteParticipantDialog": "Êtes-vous sûr(e) de vouloir couper le micro de ce participant ? Seul le participant pourra ensuite réactiver son micro à tout moment.",
|
||||
"muteParticipantsVideoDialog": "Êtes-vous sûr(e) de vouloir couper la caméra de ce participant ? Seul le participant pourra ensuite réactiver sa caméra à tout moment.",
|
||||
"muteParticipantTitle": "Couper le micro de ce participant ?",
|
||||
"muteParticipantsVideoButton": "Couper la caméra",
|
||||
"muteParticipantsVideoTitle": "Couper la caméra de ce participant?",
|
||||
"muteParticipantsVideoBody": "Vous ne pourrez pas rallumer la caméra, mais ils peuvent la rallumer à tout moment.",
|
||||
"noDropboxToken": "Pas de jeton Dropbox valide",
|
||||
"Ok": "Ok",
|
||||
"password": "Mot de passe",
|
||||
"passwordLabel": "La réunion a été verrouillée par un(e) participant(e). Veuillez entrer le $t(lockRoomPassword) pour la rejoindre.",
|
||||
"passwordNotSupported": "La définition d'un $t(lockRoomPassword) de réunion n'est pas prise en charge.",
|
||||
"passwordNotSupportedTitle": "L'ajout d'un $t(lockRoomPassword) n'est pas supporté",
|
||||
"passwordRequired": "$t(lockRoomPasswordUppercase) requis",
|
||||
"permissionErrorTitle": "Permission nécessaire",
|
||||
"permissionCameraRequiredError": "L'autorisation caméra est nécessaire pour participer aux réunions avec vidéo. Merci de l'accorder dans les paramètres",
|
||||
"permissionMicRequiredError": "L'autorisation microphone est nécessaire pour participer aux réunions avec son. Merci de l'accorder dans les paramètres",
|
||||
"popupError": "Votre navigateur bloque les fenêtres pop-up. Veuillez autoriser les fenêtres pop-up dans les paramètres de votre navigateur.",
|
||||
"popupErrorTitle": "Pop-up bloquée",
|
||||
"readMore": "plus",
|
||||
@@ -296,18 +306,24 @@
|
||||
"reservationErrorMsg": "Code d'erreur: {{code}}, message: {{msg}}",
|
||||
"retry": "Réessayer",
|
||||
"screenSharingAudio": "Partager l'audio",
|
||||
"screenSharingFailed": "Oops ! Quelque chose s'est mal passée, nous n'avons pas pu démarrer le partage d'écran !",
|
||||
"screenSharingFailed": "Houla ! Quelque chose s'est mal passé, nous n'avons pas pu démarrer le partage d'écran !",
|
||||
"screenSharingFailedTitle": "Echec du partage d'écran !",
|
||||
"screenSharingPermissionDeniedError": "Oops ! Un problème est survenu avec vos autorisations de partage d'écran. Veuillez réessayer.",
|
||||
"screenSharingPermissionDeniedError": "Oups ! Une erreur s'est produite avec vos autorisations d'extension de partage d'écran. Veuillez rafraîchir et réessayer.",
|
||||
"screenSharingPermissionDeniedError": "Houla ! Un problème est survenu avec vos autorisations de partage d'écran. Veuillez réessayer.",
|
||||
"sendPrivateMessage": "Vous avez récemment reçu un message privé. Aviez-vous l'intention d'y répondre en privé, ou vouliez-vous envoyer votre message au groupe ?",
|
||||
"sendPrivateMessageCancel": "Envoyer au groupe",
|
||||
"sendPrivateMessageOk": "Envoyer en privé",
|
||||
"sendPrivateMessageTitle": "Envoyer en privé ?",
|
||||
"serviceUnavailable": "Service indisponible",
|
||||
"sessTerminated": "Appel terminé",
|
||||
"sessionRestarted": "L'appel est relancé par le pont",
|
||||
"sessionRestarted": "L'appel est relancé par la passerelle",
|
||||
"Share": "Partager",
|
||||
"shareAudio": "Continuer",
|
||||
"shareAudioTitle" : "Comment partager le son",
|
||||
"shareAudioWarningTitle": "Vous devez cesser de partager l'écran avant de partager l'audio",
|
||||
"shareAudioWarningH1": "Si vous voulez partager uniquement de l'audio:",
|
||||
"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\".",
|
||||
"shareMediaWarningGenericH2": "Si vous voulez partager votre écran et l'audio",
|
||||
"shareVideoLinkError": "Veuillez renseigner un lien Youtube fonctionnel.",
|
||||
"shareVideoTitle": "Partager une vidéo",
|
||||
"shareYourScreen": "Partager votre écran",
|
||||
@@ -315,6 +331,11 @@
|
||||
"startLiveStreaming": "Démarrer la diffusion en direct",
|
||||
"startRecording": "Commencer l'enregistrement",
|
||||
"startRemoteControlErrorMessage": "Une erreur est survenue lors de la tentative de démarrage de la session de contrôle à distance !",
|
||||
"shareScreenWarningTitle": "Vous devez cesser de partager votre audio avant de partager votre écran",
|
||||
"shareScreenWarningH1": "Si vous voulez partager uniquement votre écran:",
|
||||
"shareScreenWarningD1": "vous devez arrêter le partage d'audio avant de partager votre écran.",
|
||||
"shareScreenWarningD2": "vous devez arrêter le partage d'audio, démarrer le partage d'écran et cocher l'option \"Partager l'audio\".",
|
||||
"sharedVideoLinkPlaceholder": "lien YouTube ou lien vidéo direct",
|
||||
"stopLiveStreaming": "Arrêter la diffusion en direct",
|
||||
"stopRecording": "Arrêter l'enregistrement",
|
||||
"stopRecordingWarning": "Désirez-vous vraiment arrêter l'enregistrement ?",
|
||||
@@ -331,9 +352,12 @@
|
||||
"userIdentifier": "Identifiant utilisateur",
|
||||
"userPassword": "mot de passe utilisateur",
|
||||
"videoLink": "Lien de la vidéo",
|
||||
"viewUpgradeOptions": "Voir les options de mise à jour",
|
||||
"viewUpgradeOptionsContent": "Pour obtenir un accès illimité à des capacités premium comme l'enregistrement, les transcriptions, diffusion RTMP et plus, vous devez mettre à jour votre plan.",
|
||||
"viewUpgradeOptionsTitle": "Vous avez découvert une capacité premium !",
|
||||
"WaitForHostMsg": "La conférence <b>{{room}}</b> n'a pas encore commencé. Si vous en êtes l'hôte, veuillez vous authentifier. Sinon, veuillez attendre son arrivée.",
|
||||
"WaitForHostMsgWOk": "La conférence <b>{{room}}</b> n'a pas encore commencé. Si vous en êtes l'hôte, veuillez appuyer sur Ok pour vous authentifier. Sinon, veuillez attendre son arrivée.",
|
||||
"WaitingForHostTitle": "En attente du hôte ...",
|
||||
"WaitingForHostTitle": "En attente de l'hôte ...",
|
||||
"Yes": "Oui",
|
||||
"yourEntireScreen": "Votre écran entier",
|
||||
"remoteUserControls": "Contrôles de l'utilisateur distant {{username}}",
|
||||
@@ -346,7 +370,7 @@
|
||||
"title": "Document partagé"
|
||||
},
|
||||
"e2ee": {
|
||||
"labelToolTip": "L'audio et la vidéo de cette conférence sont chiffrés de Bout-en-Bout"
|
||||
"labelToolTip": "Le son et la vidéo de cette conférence sont chiffrés de Bout-en-Bout"
|
||||
},
|
||||
"embedMeeting": {
|
||||
"title": "Intégrer cette réunion"
|
||||
@@ -368,9 +392,11 @@
|
||||
"image4" : "Lampadaire noir",
|
||||
"image5" : "Montagne",
|
||||
"image6" : "Forêt ",
|
||||
"image7" : "Lever du soleil",
|
||||
"image7" : "Lever de soleil",
|
||||
"desktopShareError": "Impossible de créer le partage de bureau",
|
||||
"desktopShare":"Partage de bureau"
|
||||
"desktopShare":"Partage de bureau",
|
||||
"webAssemblyWarning": "WebAssembly non supporté",
|
||||
"backgroundEffectError": "Erreur dans l'application de l'effet d'arrière-plan."
|
||||
},
|
||||
"feedback": {
|
||||
"average": "Moyen",
|
||||
@@ -405,8 +431,12 @@
|
||||
"genericError": "Oups, quelque chose a mal tourné.",
|
||||
"inviteLiveStream": "Pour voir la diffusion en direct de cette réunion, cliquez sur ce lien : {{url}}",
|
||||
"invitePhone": "Pour rejoindre depuis un téléphone, saisissez : {{number}},,{{conferenceID}}#\n",
|
||||
"invitePhoneAlternatives": "Vous cherchez un numéro d'appel différent ?\nAfficher les numéros d'appel de la réunion: {{url}}\n\n\nSi vous appelez également via un téléphone de salle, vous pouvez vous connecter sans audio: {{silentUrl}}",
|
||||
"invitePhoneAlternatives": "Vous cherchez un numéro d'appel différent ?\nAfficher les numéros d'appel de la réunion: {{url}}\n\n\nSi vous appelez également via un téléphone de réunion, vous pouvez vous connecter sans audio: {{silentUrl}}",
|
||||
"inviteSipEndpoint": "Pour rejoindre en utilisant l'adresse SIP, entrez ceci : {{sipUri}}",
|
||||
"inviteTextiOSPersonal": "{{name}} vous invite à une réunion.",
|
||||
"inviteTextiOSJoinSilent": "Si vous téléphonez avec un téléphone de réunion, utilisez ce lien pour rejoindre sans vous connecter en audio: {{silentUrl}}.",
|
||||
"inviteTextiOSInviteUrl": "Clickez le lien suivant pour rejoindre: {{inviteUrl}}.",
|
||||
"inviteTextiOSPhone": "Pour rejoindre par téléphone, utilisez ce numéro: {{number}},,{{conferenceID}}#. Si vous voulez utiliser un autre numéro, voici la liste complête: {{didUrl}}.",
|
||||
"inviteURLFirstPartGeneral": "Vous êtes invité(e) à participer à une réunion.",
|
||||
"inviteURLFirstPartPersonal": "{{name}} vous invite à une réunion.\n",
|
||||
"inviteURLSecondPart": "\nRejoindre la réunion :\n{{url}}\n",
|
||||
@@ -417,6 +447,7 @@
|
||||
"noRoom": "Aucune réunion n'a été spécifiée pour l'appel entrant.",
|
||||
"numbers": "Numéros d'appel",
|
||||
"password": "$t(lockRoomPasswordUppercase) :",
|
||||
"sip": "adresse SIP",
|
||||
"title": "Partager",
|
||||
"tooltip": "Partager le lien et les informations de connexion pour cette conférence",
|
||||
"label": "Information de la réunion"
|
||||
@@ -430,7 +461,7 @@
|
||||
"send": "Envoyer"
|
||||
},
|
||||
"inlineDialogFailure": {
|
||||
"msg": "Nous avons trébuché un peu.",
|
||||
"msg": "Il y a eu un petit problème.",
|
||||
"retry": "Réessayer",
|
||||
"support": "Support",
|
||||
"supportMsg": "Si ceci persiste, contactez"
|
||||
@@ -454,6 +485,8 @@
|
||||
"videoMute": "Démarrer / Arrêter votre caméra"
|
||||
},
|
||||
"liveStreaming": {
|
||||
"limitNotificationDescriptionWeb": "En raison d'une forte demande, votre diffusion sera limitée à {{limit}} min. Pour une diffusion illimitée, essayez <a href={{url}} rel='noopener noreferrer' target='_blank'> {{app}} </a>.",
|
||||
"limitNotificationDescriptionNative": "Votre diffusion sera limitée à {{limit}} min. Pour une diffusion illimitée, essayez {{app}}.",
|
||||
"busy": "Nous tentons de libérer des ressources de diffusion. Veuillez réessayez dans quelques minutes.",
|
||||
"busyTitle": "Tous les flux sont actuellement occupés",
|
||||
"changeSignIn": "Changer de compte.",
|
||||
@@ -461,8 +494,6 @@
|
||||
"chooseCTA": "Choisissez une option de diffusion. Vous êtes actuellement connecté comme {{email}}.",
|
||||
"enterStreamKey": "Entrez votre clé de diffusion Youtube ici",
|
||||
"error": "La diffusion a échouée. Veuillez réessayer ultérieurement.",
|
||||
"enterStreamKey": "Entrez votre clé de flux direct YouTube ici.",
|
||||
"error": "Le Streaming a échoué. Veuillez réessayer.",
|
||||
"errorAPI": "Une erreur s'est produite lors de l'accès à vos diffusions YouTube. Veuillez réessayer de vous connecter.",
|
||||
"errorLiveStreamNotEnabled": "La diffusion en direct n'est pas activée pour {{email}}. Merci de l'activer ou de vous connecter avec un compte où elle est déjà activée.",
|
||||
"expandedOff": "La diffusion en direct a été arrêtée",
|
||||
@@ -471,8 +502,6 @@
|
||||
"failedToStart": "La diffusion n'a pas réussi à démarrer",
|
||||
"getStreamKeyManually": "Nous n'avons pas réussi à récupérer un flux de direct. Essayez d'obtenir votre clé de diffusion en direct sur YouTube.",
|
||||
"invalidStreamKey": "La clé de diffusion en direct n'est peut-être pas correcte.",
|
||||
"limitNotificationDescriptionWeb": "En raison d'une forte demande, votre diffusion sera limitée à {{limit}} min. Pour un streaming illimité, essayez <a href={{url}} rel='noopener noreferrer' target='_blank'> {{app}} </a>.",
|
||||
"limitNotificationDescriptionNative": "Votre diffusion sera limitée à {{limit}} min. Pour un streaming illimité, essayez {{app}}.",
|
||||
"off": "La diffusion en direct (streaming) a été arrêté",
|
||||
"offBy": "{{name}} a arrêté la diffusion en direct",
|
||||
"on": "En direct",
|
||||
@@ -509,7 +538,7 @@
|
||||
"finishedModerator": "L'enregistrement de la session {{token}} s'est terminé. L'enregistrement a bien été sauvegardée. Merci de demander aux autres participants de soumettre leurs enregistrements.",
|
||||
"notModerator": "Vous n'êtes pas le modérateur. Vous ne pouvez pas démarrer ou arrêter un enregistrement local."
|
||||
},
|
||||
"moderator": "Modérateur ",
|
||||
"moderator": "Droits de modérateur accordés",
|
||||
"no": "Non",
|
||||
"participant": "Participant(e)",
|
||||
"participantStats": "Statistiques du participant",
|
||||
@@ -522,13 +551,15 @@
|
||||
"lockRoomPasswordUppercase": "Mot de passe",
|
||||
"me": "moi",
|
||||
"notify": {
|
||||
"allowAction": "Permettre",
|
||||
"allowedUnmute": "Vous pouvez réactiver votre écran, votre caméra ou partager votre écran.",
|
||||
"connectedOneMember": "{{name}} a rejoint la réunion",
|
||||
"connectedThreePlusMembers": "{{name}} et {{count}} autres personnes ont rejoint la réunion",
|
||||
"connectedTwoMembers": "{{first}} et {{second}} ont rejoint la réunion",
|
||||
"disconnected": "déconnecté",
|
||||
"focus": "Focus de conférence",
|
||||
"focusFail": "{{component}} n'est pas disponible - réessayez dans {{ms}} sec",
|
||||
"grantedTo": "Droits modérateur accordés à {{to}} !",
|
||||
"hostAskedUnmute": "Le modérateur souhaite vous donner la parole",
|
||||
"invitedOneMember": "{{name}} a été invité(e)",
|
||||
"invitedThreePlusMembers": "{{name}} et {{count}} autres ont été invités",
|
||||
"invitedTwoMembers": "{{first}} et {{second}} ont été invités",
|
||||
@@ -549,42 +580,60 @@
|
||||
"somebody": "Quelqu'un",
|
||||
"startSilentTitle": "Vous avez rejoint sans sortie audio !",
|
||||
"startSilentDescription": "Rejoignez la réunion de nouveau pour activer l'audio",
|
||||
"suboptimalBrowserWarning": "Nous craignons que votre expérience de réunion en ligne ne soit bonne ici. Nous cherchons des moyens d'améliorer cela, mais d'ici-là, essayez d'utiliser l'un des <a href='{{recommendedBrowserPageLink}}' target='_blank'>navigateurs supportés</a>.",
|
||||
"suboptimalBrowserWarning": "Nous craignons que votre expérience de réunion en ligne ne soit pas idéale ici. Nous cherchons des moyens d'améliorer cela, mais d'ici-là, essayez d'utiliser l'un des <a href='{{recommendedBrowserPageLink}}' target='_blank'>navigateurs supportés</a>.",
|
||||
"suboptimalExperienceTitle": "Avertissement du navigateur",
|
||||
"unmute": "Rétablir le son",
|
||||
"newDeviceCameraTitle": "Nouvelle caméra détectée",
|
||||
"newDeviceAudioTitle": "Nouveau périphérique audio détecté",
|
||||
"newDeviceAction": "Utiliser",
|
||||
"OldElectronAPPTitle": "Vulnérabilité de sécurité !",
|
||||
"oldElectronClientDescription1": "Vous semblez utiliser une ancienne version du client Jitsi Meet qui présente des vulnérabilités de sécurité connues. Veuillez vous assurer de mettre à jour vers notre ",
|
||||
"OldElectronAPPTitle": "Faille de sécurité !",
|
||||
"oldElectronClientDescription1": "Vous semblez utiliser une ancienne version du client Jitsi Meet qui présente des failles de sécurité connues. Veuillez vous assurer de mettre à jour vers notre ",
|
||||
"oldElectronClientDescription2": "dernière build",
|
||||
"oldElectronClientDescription3": " rapidement !",
|
||||
"moderationInEffectDescription": "Merci de levez la main pour demander la parole.",
|
||||
"moderationInEffectCSDescription": "Merci de lever la main si vous voulez partager votre écran.",
|
||||
"moderationInEffectVideoDescription": "Merci de lever la main si vous souhaitez démarrer votre caméra.",
|
||||
"moderationInEffectTitle": "Votre micro est coupé par le modérateur",
|
||||
"moderationInEffectCSTitle": "Le partage d'écran est interdit par le modérateur",
|
||||
"moderationInEffectVideoTitle": "Votre caméra est coupée par le modérateur",
|
||||
"moderationRequestFromModerator": "Le modérateur souhaite que vous activiez votre micro",
|
||||
"moderationRequestFromParticipant": "Souhaite parler",
|
||||
"moderationStartedTitle": "Modération démarrée",
|
||||
"moderationStoppedTitle": "Modération arrêtée",
|
||||
"moderationToggleDescription": "par {{participantDisplayName}}",
|
||||
"raiseHandAction": "Lever la main",
|
||||
"reactionSounds": "Bloquer les réactions sonores",
|
||||
"groupTitle": "Notifications"
|
||||
},
|
||||
"participantsPane": {
|
||||
"close": "Fermer",
|
||||
"header": "Participants",
|
||||
"headings": {
|
||||
"lobby": "Salle d'attente ({{count}})",
|
||||
"participantsList": "Participants de la réunion ({{count}})",
|
||||
"waitingLobby": "Dans la salle d'attente ({{count}})"
|
||||
},
|
||||
"actions": {
|
||||
"allow": "Autoriser les participant à:",
|
||||
"allow": "Autoriser les participants à:",
|
||||
"allowVideo": "permettre la vidéo",
|
||||
"audioModeration": "réouvrir leur micro",
|
||||
"blockEveryoneMicCamera": "Bloquer tous les micros et caméras",
|
||||
"invite": "Inviter quelqu'un",
|
||||
"askUnmute": "Demander de réactiver le micro",
|
||||
"mute": "Couper le micro",
|
||||
"muteAll": "Couper le micro de tout le monde",
|
||||
"muteEveryoneElse": "Couper le micro de tous les autres",
|
||||
"startModeration": "Réactiver son micro ou démarrer sa vidéo",
|
||||
"stopEveryonesVideo": "Couper toutes les caméras",
|
||||
"stopVideo": "Couper la vidéo",
|
||||
"unblockEveryoneMicCamera": "Débloquer tous les micros et caméras"
|
||||
}
|
||||
"unblockEveryoneMicCamera": "Débloquer tous les micros et caméras",
|
||||
"videoModeration": "Démarrer leur vidéo"
|
||||
},
|
||||
"search": "Rechercher des participants"
|
||||
},
|
||||
"passwordSetRemotely": "défini par un autre participant",
|
||||
"passwordDigitsOnly": "Jusqu'à {{number}} chiffres",
|
||||
"polls": {
|
||||
"by": "Par {{ name }}",
|
||||
"create": {
|
||||
"addOption": "Ajouter une option",
|
||||
"answerPlaceholder": "Option {{index}}",
|
||||
@@ -609,7 +658,7 @@
|
||||
},
|
||||
"notification": {
|
||||
"title": "Un nouveau sondage a été ajouté à la réunion",
|
||||
"description": "Ouvrez l'onget des sondages pour voter"
|
||||
"description": "Ouvrez l'onglet des sondages pour voter"
|
||||
}
|
||||
},
|
||||
"poweredby": "produit par",
|
||||
@@ -629,8 +678,8 @@
|
||||
"poor": "Vous avez une mauvaise connexion"
|
||||
},
|
||||
"connectionDetails": {
|
||||
"audioClipping": "Attendez vous à ce que votre audio soit coupé.",
|
||||
"audioHighQuality": "Votre audio sera de bonne qualité.",
|
||||
"audioClipping": "Attendez vous à ce que votre son soit coupé.",
|
||||
"audioHighQuality": "Votre son sera de bonne qualité.",
|
||||
"audioLowNoVideo": "Attendez vous à une faible qualité audio et aucune vidéo",
|
||||
"goodQuality": "Impressionnant ! La qualité de vos médias sera excellente",
|
||||
"noMediaConnectivity": "Nous n'avons pas pu trouver un moyen d'établir une connectivité multimédia pour ce test. Cela est généralement causé par un pare-feu ou un NAT.",
|
||||
@@ -652,6 +701,7 @@
|
||||
"errorDialOutFailed": "Impossible de composer le numéro. L'appel a échoué",
|
||||
"errorDialOutStatus": "Erreur lors de l'obtention de l'état d'appel sortant",
|
||||
"errorMissingName": "Veuillez entrer votre nom pour entrer en conférence",
|
||||
"errorNoPermissions": "Vous devez permettre l'accès microphone et caméra",
|
||||
"errorStatusCode": "Erreur de numérotation, code d'état: {{status}}",
|
||||
"errorValidation": "La validation du numéro a échoué",
|
||||
"iWantToDialIn": "Je veux me connecter",
|
||||
@@ -662,7 +712,6 @@
|
||||
"linkCopied": "Lien copié dans le presse-papiers",
|
||||
"lookGood": "Il semble que votre microphone fonctionne correctement",
|
||||
"or": "ou",
|
||||
"calling": "Appel",
|
||||
"premeeting": "Pré-séance",
|
||||
"showScreen": "Activer l'écran de pré-séance",
|
||||
"startWithPhone": "Commencez avec l'audio du téléphone",
|
||||
@@ -695,19 +744,22 @@
|
||||
},
|
||||
"raisedHand": "Aimerait prendre la parole",
|
||||
"recording": {
|
||||
"limitNotificationDescriptionWeb": "En raison d'une forte demande, votre enregistrement sera limité à {{limit}} min. Pour des enregistrements illimités, essayez <a href={{url}} rel='noopener noreferrer' target='_blank'> {{app}} </a>.",
|
||||
"limitNotificationDescriptionNative": "En raison d'une forte demande, votre enregistrement sera limité à {{limit}} min. Pour des enregistrements illimités, essayez <3> {{app}} </3>.",
|
||||
"authDropboxText": "Téléchargement vers Dropbox",
|
||||
"availableSpace": "Espace disponible : {{spaceLeft}} Mo (approximativement {{duration}} minutes d'enregistrement)",
|
||||
"beta": "BETA",
|
||||
"busy": "Nous sommes en train de libérer les ressources d'enregistrement. Réessayez dans quelques minutes.",
|
||||
"busyTitle": "Tous les enregistreurs sont actuellement occupés",
|
||||
"copyLink": "Copier lien",
|
||||
"error": "Échec de l'enregistrement. Veuillez réessayer.",
|
||||
"errorFetchingLink": "Erreur de récupération du lien d'enregistrement.",
|
||||
"expandedOff": "L'enregistrement a été arrêté",
|
||||
"expandedOn": "Cette conférence est actuellement en cours d'enregistrement.",
|
||||
"expandedPending": "Démarrage de l'enregistrement ...",
|
||||
"failedToStart": "L'enregistrement n'a pas réussi à démarrer",
|
||||
"fileSharingdescription": "Partager l'enregistrement avec les participants de la réunion",
|
||||
"limitNotificationDescriptionWeb": "En raison d'une forte demande, votre enregistrement sera limité à {{limit}} min. Pour des enregistrements illimités, essayez <a href={{url}} rel='noopener noreferrer' target='_blank'> {{app}} </a>.",
|
||||
"limitNotificationDescriptionNative": "En raison d'une forte demande, votre enregistrement sera limité à {{limit}} min. Pour des enregistrements illimités, essayez <3> {{app}} </3>.",
|
||||
"linkGenerated": "Nous avons généré un lien à votre enregistrement.",
|
||||
"live": "DIRECT",
|
||||
"loggedIn": "Connecté en tant que {{userName}}",
|
||||
"off": "Enregistrement arrêté",
|
||||
@@ -722,27 +774,33 @@
|
||||
"signIn": "Se connecter",
|
||||
"signOut": "Se déconnecter",
|
||||
"unavailable": "Oups ! Le {{serviceName}} est actuellement indisponible. Nous tentons de résoudre le problème. Veuillez réessayer plus tard.",
|
||||
"unavailableTitle": "Enregistrement indisponible"
|
||||
"unavailableTitle": "Enregistrement indisponible",
|
||||
"uploadToCloud": "Envoyer vers le cloud"
|
||||
},
|
||||
"sectionList": {
|
||||
"pullToRefresh": "Tirer pour recharger"
|
||||
},
|
||||
"security": {
|
||||
"about": "Vous pouvez ajouter un mot de passe à votre réunion. Les participants devront fournir le mot de passe avant qu'ils soient autorisés à rejoindre la réunion.",
|
||||
"aboutReadOnly": "Les modérateurs peuvent ajouter un mot de passe à la réunion. Les participants devront fournir le mot de passe avant qu'ils soient autorisés à rejoindre la réunion.",
|
||||
"about": "Vous pouvez ajouter un mot de passe à votre réunion. Les participants devront fournir le mot de passe avant de pouvoir rejoindre la réunion.",
|
||||
"aboutReadOnly": "Les modérateurs peuvent ajouter un mot de passe à la réunion. Les participants devront fournir le mot de passe avant de pouvoir rejoindre la réunion.",
|
||||
"insecureRoomNameWarning": "Le nom de la salle est peu sûr. Des participants non désirés peuvent rejoindre votre réunion. Pensez à sécuriser votre réunion en cliquant sur le bouton de sécurité.",
|
||||
"securityOptions": "Options de sécurité"
|
||||
},
|
||||
"settings": {
|
||||
"calendar": {
|
||||
"about": "L'intégration de {{appName}} avec votre calendrier permet d'accéder de manière sécurisée aux événement à venir.",
|
||||
"about": "L'intégration de {{appName}} avec votre calendrier permet d'accéder de manière sécurisée aux événements à venir.",
|
||||
"disconnect": "Se déconnecter",
|
||||
"microsoftSignIn": "Se connecter avec Microsoft",
|
||||
"signedIn": "Accès aux événements du calendrier {{email}}. Cliquez sur le bouton se déconnecter ci-dessous pour arrêter l'accès aux événements du calendrier.",
|
||||
"title": "Calendrier"
|
||||
},
|
||||
"desktopShareFramerate": "Images par seconde pour le Partage d'écran",
|
||||
"desktopShareWarning": "Vous devez repartager l'écran pour que ces paramètres soient utilisés.",
|
||||
"desktopShareHighFpsWarning": "Augmenter le nombre d'images par seconde pour le partage d'écran peut impacter votre bande passante. Vous devez repartager l'écran pour que ces paramètres soient utilisés.",
|
||||
"devices": "Périphériques",
|
||||
"followMe": "Tout le monde me suit",
|
||||
"framesPerSecond": "images par seconde",
|
||||
"incomingMessage": "un message arrive",
|
||||
"language": "Langue",
|
||||
"loggedIn": "Connecté en tant que {{name}}",
|
||||
"microphones": "Microphones",
|
||||
@@ -750,13 +808,19 @@
|
||||
"more": "Plus",
|
||||
"name": "Nom",
|
||||
"noDevice": "Aucun",
|
||||
"participantJoined": "un participant arrive",
|
||||
"participantLeft": "un participant quitte",
|
||||
"playSounds": "Jouer un son quand",
|
||||
"reactions": "il y a une réaction à la réunion",
|
||||
"sameAsSystem": "Identique au système ({{label}})",
|
||||
"selectAudioOutput": "Sortie audio",
|
||||
"selectCamera": "Caméra",
|
||||
"selectMic": "Microphone",
|
||||
"speakers": "Intervenants",
|
||||
"sounds": "Sons",
|
||||
"speakers": "Haut-parleurs",
|
||||
"startAudioMuted": "Tout le monde commence en muet",
|
||||
"startVideoMuted": "Tout le monde commence sans vidéo",
|
||||
"talkWhileMuted": "vous parlez en étant muet",
|
||||
"title": "Paramètres"
|
||||
},
|
||||
"settingsView": {
|
||||
@@ -787,6 +851,7 @@
|
||||
},
|
||||
"speaker": "Haut-parleur",
|
||||
"speakerStats": {
|
||||
"search": "Recherche",
|
||||
"hours": "{{count}}h",
|
||||
"minutes": "{{count}}m",
|
||||
"name": "Nom",
|
||||
@@ -808,19 +873,23 @@
|
||||
"accessibilityLabel": {
|
||||
"audioOnly": "Activer / Désactiver le mode voix uniquement",
|
||||
"audioRoute": "Sélectionner la source audio",
|
||||
"boo": "Hou",
|
||||
"callQuality": "Ajuster la qualité vidéo",
|
||||
"cc": "Activer / Désactiver les sous-titres",
|
||||
"chat": "Afficher / Masquer la discussion instantanée",
|
||||
"clap": "Applaudir",
|
||||
"document": "Activer / Désactiver le document partagé",
|
||||
"download": "Télécharger nos applications",
|
||||
"embedMeeting": "Intégrer la réunion",
|
||||
"feedback": "Laisser des commentaires",
|
||||
"fullScreen": "Activer / Désactiver le plein écran",
|
||||
"grantModerator": "Nommer modérateur",
|
||||
"grantModerator": "donner des droits de modérateur",
|
||||
"hangup": "Quitter la conversation",
|
||||
"help": "Aide",
|
||||
"invite": "Inviter des participants",
|
||||
"kick": "Expulser le participant",
|
||||
"laugh": "Rire",
|
||||
"like": "Approuver",
|
||||
"lobbyButton": "Activer / Désactiver le mode salle d'attente",
|
||||
"localRecording": "Activer / Désactiver les contrôles d'enregistrement local",
|
||||
"lockRoom": "Activer / Désactiver le mot de passe de la réunion",
|
||||
@@ -837,6 +906,7 @@
|
||||
"privateMessage": "Envoyer un message privé",
|
||||
"profile": "Éditer votre profil",
|
||||
"raiseHand": "Lever la main",
|
||||
"reactionsMenu": "Ouvrir / fermer le menu réactions",
|
||||
"recording": "Activer / Désactiver l'enregistrement",
|
||||
"remoteMute": "Couper le micro du participant",
|
||||
"remoteVideoMute": "Couper la caméra du participant",
|
||||
@@ -848,7 +918,9 @@
|
||||
"shareYourScreen": "Activer / Désactiver le partage d'écran",
|
||||
"shortcuts": "Afficher / Masquer les raccourcis",
|
||||
"show": "Afficher en premier plan",
|
||||
"silence": "Silence",
|
||||
"speakerStats": "Afficher / Cacher les statistiques de parole",
|
||||
"surprised": "Surpris",
|
||||
"tileView": "Activer / Désactiver la vue mosaïque",
|
||||
"toggleCamera": "Changer de caméra",
|
||||
"toggleFilmstrip": "Basculer de pellicule",
|
||||
@@ -860,13 +932,18 @@
|
||||
},
|
||||
"addPeople": "Ajouter des personnes à votre appel",
|
||||
"audioSettings": "Paramètres audio",
|
||||
"videoSettings": "Paramètres vidéo",
|
||||
"audioOnlyOff": "Désactiver le mode bande passante réduite",
|
||||
"audioOnlyOn": "Activer le mode bande passante réduite",
|
||||
"audioRoute": "Sélectionner la source audio",
|
||||
"authenticate": "Authentifiez-vous",
|
||||
"boo": "Huer",
|
||||
"callQuality": "Ajuster la qualité vidéo",
|
||||
"chat": "Ouvrir / Fermer le chat",
|
||||
"clap": "Applaudir",
|
||||
"closeChat": "Fermer le chat",
|
||||
"closeReactionsMenu": "Fermer le menu réactions",
|
||||
"disableReactionSounds": "Vous pouvez interdire les réactions sonores à cette réunion",
|
||||
"documentClose": "Fermer le document partagé",
|
||||
"documentOpen": "Ouvrir le document partagé",
|
||||
"download": "Télécharger nos applications",
|
||||
@@ -880,6 +957,8 @@
|
||||
"hangup": "Quitter",
|
||||
"help": "Aide",
|
||||
"invite": "Inviter des participants",
|
||||
"laugh": "Rire",
|
||||
"like": "Approuver",
|
||||
"lobbyButtonDisable": "Désactiver le mode salle d'attente / contrôle des participant(e)s",
|
||||
"lobbyButtonEnable": "Activer le mode salle d'attente / contrôle des participant(e)s",
|
||||
"login": "Connexion",
|
||||
@@ -896,31 +975,40 @@
|
||||
"noAudioSignalDialInDesc": "Vous pouvez également appeler en utilisant :",
|
||||
"noAudioSignalDialInLinkDesc": "Numéros d'appel",
|
||||
"noisyAudioInputTitle": "Votre microphone semble être bruyant !",
|
||||
"noisyAudioInputDesc": "Il semble que votre microphone fasse du bruit, veuillez le couper ou changer de périphérique.",
|
||||
"noisyAudioInputDesc": "Il semble que votre microphone soit bruyant, veuillez le couper ou changer de périphérique.",
|
||||
"openChat": "Ouvrir le chat",
|
||||
"openReactionsMenu": "Ouvrir le menu Réactions",
|
||||
"participants": "Participants",
|
||||
"pip": "Entrer en mode Picture-in-Picture",
|
||||
"privateMessage": "Envoyer un message privé",
|
||||
"profile": "Éditer votre profil",
|
||||
"raiseHand": "Lever / Baisser la main",
|
||||
"raiseYourHand": "Lever la main",
|
||||
"reactionBoo": "Envoyer réaction huer",
|
||||
"reactionClap": "Envoyer réaction applaudir",
|
||||
"reactionLaugh": "Envoyer réaction rire",
|
||||
"reactionLike": "Envoyer réaction approuver",
|
||||
"reactionSilence": "Envoyer réaction silence",
|
||||
"reactionSurprised": "Envoyer réaction surprise",
|
||||
"security": "Options de sécurité",
|
||||
"Settings": "Paramètres",
|
||||
"shareaudio": "Partager l'audio",
|
||||
"sharedvideo": "Partager une vidéo YouTube",
|
||||
"shareRoom": "Inviter quelqu'un",
|
||||
"shortcuts": "Afficher les raccourcis",
|
||||
"silence": "Silence",
|
||||
"speakerStats": "Statistiques de l'interlocuteur",
|
||||
"startScreenSharing": "Démarrer le partage d'écran",
|
||||
"startSubtitles": "Activer les sous-titres",
|
||||
"stopAudioSharing": "Arrêter le partage son",
|
||||
"stopScreenSharing": "Arrêter le partage d'écran",
|
||||
"stopSubtitles": "Désactiver les sous-titres",
|
||||
"stopSharedVideo": "Arrêter la vidéo YouTube",
|
||||
"surprised": "Surpris",
|
||||
"talkWhileMutedPopup": "Vous voulez parler ? Votre micro est coupé.",
|
||||
"tileViewToggle": "Activer / Désactiver la vue mosaïque",
|
||||
"toggleCamera": "Changer de caméra",
|
||||
"videomute": "Démarrer / Arrêter la caméra",
|
||||
"videoSettings": "Paramètres vidéo",
|
||||
"selectBackground": "Sélectionner un arrière-plan"
|
||||
},
|
||||
"transcribing": {
|
||||
@@ -959,7 +1047,10 @@
|
||||
"pending": "{{displayName}} a été invité(e)"
|
||||
},
|
||||
"videoStatus": {
|
||||
"adjustFor": "Ajuster pour:",
|
||||
"audioOnly": "VOIX",
|
||||
"bestPerformance": "la meilleure performance",
|
||||
"highestQuality": "la meilleure qualité",
|
||||
"audioOnlyExpanded": "Vous êtes en mode bande passante réduite. Dans ce mode, vous ne recevrez que le partage audio et le partage d'écran.",
|
||||
"callQuality": "Qualité vidéo",
|
||||
"hd": "HD",
|
||||
@@ -970,6 +1061,7 @@
|
||||
"ld": "BD",
|
||||
"ldTooltip": "Regardez la vidéo en basse définition",
|
||||
"lowDefinition": "Basse définition",
|
||||
"performanceSettings": "Paramètres de performance",
|
||||
"sd": "MD",
|
||||
"sdTooltip": "Regardez la vidéo en définition standard",
|
||||
"standardDefinition": "Moyenne Définition"
|
||||
@@ -981,7 +1073,7 @@
|
||||
"domuteOthers": "Couper le micro de tous les autres",
|
||||
"domuteVideoOfOthers": "Couper la caméra des autres",
|
||||
"flip": "Balancer",
|
||||
"grantModerator": "Nommer modérateur",
|
||||
"grantModerator": "Donner des droits de modérateur",
|
||||
"kick": "Exclure",
|
||||
"moderator": "Modérateur",
|
||||
"mute": "Un participant a coupé son micro",
|
||||
@@ -1061,6 +1153,7 @@
|
||||
"enableDialogText": "Le mode salle d'attente vous permet de protéger votre réunion en autorisant les personnes à entrer qu'après l'approbation formelle d'un modérateur.",
|
||||
"enterPasswordButton": "Saisissez un mot de passe de réunion",
|
||||
"enterPasswordTitle": "Saisissez le mot de passe pour rejoindre la réunion",
|
||||
"errorMissingPassword": "Veuillez saisir le mot de passe de la réunion",
|
||||
"invalidPassword": "Mot de passe invalide",
|
||||
"joiningMessage": "Vous allez rejoindre une réunion dès que quelqu'un aura accepté votre demande",
|
||||
"joinWithPasswordMessage": "Tentative de rejoindre avec mot de passe, patientez s'il vous plait ...",
|
||||
@@ -1079,6 +1172,7 @@
|
||||
"passwordField": "Veuillez saisir le mot de passe de la réunion",
|
||||
"passwordJoinButton": "Rejoindre",
|
||||
"reject": "Refuser",
|
||||
"rejectAll": "Refuser tout",
|
||||
"toggleLabel": "Activer la salle d'attente"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -327,11 +327,11 @@
|
||||
"muteEveryoneDialogModerationOn": "Los participants pòdon enviar una requèsta per parlar quand vòlgan.",
|
||||
"muteEveryoneElsesVideoTitle": "Arrestar la vidèo de tot lo monde levat {{whom}} ?",
|
||||
"muteEveryonesVideoTitle": "Arrestar la vidèo de tot lo monde ?",
|
||||
"muteParticipantsVideoButton": "Arrestar la camèra",
|
||||
"muteParticipantsVideoButton": "Arrestar la vidèo",
|
||||
"muteParticipantsVideoTitle": "Desactivar la camèra d’aqueste participant ?",
|
||||
"noDropboxToken": "Cap de geton Dropbox pas valid",
|
||||
"password": "Senhal",
|
||||
"sessionRestarted": "Sonada reaviada pel pont",
|
||||
"sessionRestarted": "Sonada reaviada a causa d’un problèma de connexion",
|
||||
"shareAudio": "Contunhar",
|
||||
"shareAudioTitle": "Cossí partejar l’àudio",
|
||||
"shareAudioWarningTitle": "Devètz arrestar lo partiment d’ecran abans lo partiment d’àudio",
|
||||
@@ -883,8 +883,8 @@
|
||||
"clap": "Picar de las mans",
|
||||
"laugh": "Rire",
|
||||
"like": "Levar lo det gròs",
|
||||
"muteEveryonesVideo": "Copar la camèra dels autres",
|
||||
"muteEveryoneElsesVideo": "Copar la camèra de los demai",
|
||||
"muteEveryonesVideo": "Copar la vidèo del monde",
|
||||
"muteEveryoneElsesVideo": "Copar la vidèo de los demai",
|
||||
"participants": "Participants",
|
||||
"remoteVideoMute": "Copar la camèra del participant",
|
||||
"shareaudio": "Partejar l’àudio",
|
||||
@@ -1025,7 +1025,11 @@
|
||||
"lowDefinition": "Bassa definicion",
|
||||
"sd": "SD",
|
||||
"sdTooltip": "Difusion vidèo en definicion estandard",
|
||||
"standardDefinition": "Definicion estandard"
|
||||
"standardDefinition": "Definicion estandard",
|
||||
"performanceSettings": "Paramètres de performança",
|
||||
"adjustFor": "Ajustar per :",
|
||||
"bestPerformance": "Melhora performança",
|
||||
"highestQuality": "Qualitat mai nauta"
|
||||
},
|
||||
"videothumbnail": {
|
||||
"connectionInfo": "Info connexion",
|
||||
@@ -1137,7 +1141,8 @@
|
||||
"unblockEveryoneMicCamera": "Desblocar lo microfòn e la camèra de tot lo monde",
|
||||
"videoModeration": "Aviar lor vidèo",
|
||||
"allowVideo": "Autorizar la vidèo"
|
||||
}
|
||||
},
|
||||
"search": "Cercar participants"
|
||||
},
|
||||
"jitsiHome": "{{logo}} Logotipe, mena a la pagina d'acuèlh",
|
||||
"polls": {
|
||||
|
||||
@@ -268,12 +268,12 @@
|
||||
"muteEveryoneStartMuted": "A partir de agora, toda a gente começa a ficar calada",
|
||||
"muteParticipantBody": "Não poderá reativá-los, mas eles podem reativar-se a qualquer momento.",
|
||||
"muteParticipantButton": "Silenciar",
|
||||
"muteParticipantDialog": "Tem a certeza de que quer silenciar este participante? Não poderá reativá-los, mas eles podem reativar-se a qualquer momento.",
|
||||
"muteParticipantsVideoDialog": "Tem a certeza de que quer desativar a câmara deste participante? Não poderá voltar a ativar a câmara, mas eles podem voltar a reativá-la a qualquer momento.",
|
||||
"muteParticipantTitle": "Silenciar este participante?",
|
||||
"muteParticipantsVideoDialogModerationOn": "Tem a certeza de que quer desligar a câmara deste participante? Não será capaz de voltar a ligar a câmara e eles também não.",
|
||||
"muteParticipantsVideoButton": "Parar vídeo",
|
||||
"muteParticipantsVideoTitle": "Desativar a câmara deste participante?",
|
||||
"muteParticipantsVideoBody": "Não poderá voltar a ligar a câmara, mas eles podem voltar a ligá-la a qualquer momento.",
|
||||
"muteParticipantsVideoBodyModerationOn": "Não será capaz de voltar a ligar a câmara e eles também não.",
|
||||
"noDropboxToken": "Nenhum token do Dropbox válido",
|
||||
"Ok": "OK",
|
||||
"password": "Palavra-passe",
|
||||
@@ -377,7 +377,7 @@
|
||||
},
|
||||
"virtualBackground": {
|
||||
"apply": "Aplicar",
|
||||
"title": "Planos de Fundo virtuais",
|
||||
"title": "Planos de fundo virtuais",
|
||||
"blur": "Desfocagem",
|
||||
"slightBlur": "Desfocagem ligeira",
|
||||
"removeBackground": "Remover imagem de fundo",
|
||||
@@ -568,9 +568,9 @@
|
||||
"moderator": "É agora um moderador",
|
||||
"muted": "Você iniciou uma conversa com o microfone desativado.",
|
||||
"mutedTitle": "Você está silenciado!",
|
||||
"mutedRemotelyTitle": "Foi silenciado pelo {{moderador}}",
|
||||
"mutedRemotelyTitle": "Foi silenciado pelo {{participantDisplayName}}",
|
||||
"mutedRemotelyDescription": "Pode sempre voltar a ativar o microfone quando estiver pronto para falar. Silencie de volta quando estiver pronto para manter o barulho afastado da reunião.",
|
||||
"videoMutedRemotelyTitle": "A sua câmara foi desligada pelo {{moderador}}.",
|
||||
"videoMutedRemotelyTitle": "A sua câmara foi desligada pelo {{participantDisplayName}}.",
|
||||
"videoMutedRemotelyDescription": "Pode sempre ligá-la novamente.",
|
||||
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) removido por outro participante",
|
||||
"passwordSetRemotely": "$t(lockRoomPasswordUppercase) definido por outro participante",
|
||||
@@ -616,22 +616,25 @@
|
||||
"actions": {
|
||||
"allow": "Permitir aos participantes:",
|
||||
"allowVideo": "Permitir vídeo",
|
||||
"audioModeration": "Eles próprios ativam o som",
|
||||
"audioModeration": "Ativarem o microfone deles",
|
||||
"blockEveryoneMicCamera": "Bloquear o microfone e a câmara de todos",
|
||||
"invite": "Convidar alguém",
|
||||
"askUnmute": "Pedir para ligar o microfone",
|
||||
"moreModerationActions": "Mais opções de moderação",
|
||||
"mute": "Silenciar",
|
||||
"muteAll": "Silenciar todos",
|
||||
"muteEveryoneElse": "Silenciar todos os outros",
|
||||
"stopEveryonesVideo": "Desligar a câmara de todos",
|
||||
"stopVideo": "Desligar a câmara",
|
||||
"unblockEveryoneMicCamera": "Desbloquear o microfone e a câmara de todos",
|
||||
"videoModeration": "Ligar a câmara deles"
|
||||
}
|
||||
"videoModeration": "Ligarem a câmara deles"
|
||||
},
|
||||
"search": "Pesquisar participantes"
|
||||
},
|
||||
"passwordSetRemotely": "Definido por outro participante",
|
||||
"passwordDigitsOnly": "Até {{number}} dígitos",
|
||||
"polls": {
|
||||
"by": "Por {{ name }}",
|
||||
"create": {
|
||||
"addOption": "Adicionar opção",
|
||||
"answerPlaceholder": "Opção {{index}}",
|
||||
@@ -1045,7 +1048,10 @@
|
||||
"pending": "{{displayName}} foi convidado"
|
||||
},
|
||||
"videoStatus": {
|
||||
"adjustFor": "Ajustar para:",
|
||||
"audioOnly": "AUD",
|
||||
"bestPerformance": "Melhor desempenho",
|
||||
"highestQuality": "Máxima qualidade",
|
||||
"audioOnlyExpanded": "Está em modo de baixa largura de banda. Neste modo, receberá apenas partilha de áudio e ecrã.",
|
||||
"callQuality": "Qualidade de vídeo",
|
||||
"hd": "HD",
|
||||
@@ -1056,6 +1062,7 @@
|
||||
"ld": "LD",
|
||||
"ldTooltip": "Ver vídeo em baixa definição",
|
||||
"lowDefinition": "Baixa definição (LD)",
|
||||
"performanceSettings": "Definições de desempenho",
|
||||
"sd": "SD",
|
||||
"sdTooltip": "Ver vídeo em definição padrão",
|
||||
"standardDefinition": "Definição padrão"
|
||||
|
||||
@@ -238,10 +238,10 @@
|
||||
"internalError": "Что-то пошло не так. Ошибка: {{error}}",
|
||||
"internalErrorTitle": "Внутренняя ошибка",
|
||||
"kickMessage": "Вы можете связаться с {{participantDisplayName}} для получения более подробной информации.",
|
||||
"kickParticipantButton": "Выгнать",
|
||||
"kickParticipantDialog": "Вы уверены, что хотите выгнать этого участника?",
|
||||
"kickParticipantTitle": "Выгнать этого участника?",
|
||||
"kickTitle": "Ай! {{participantDisplayName}} выгнал вас из конференции.",
|
||||
"kickParticipantButton": "Отключить",
|
||||
"kickParticipantDialog": "Вы уверены, что хотите отключить этого участника?",
|
||||
"kickParticipantTitle": "Отключить этого участника?",
|
||||
"kickTitle": "{{participantDisplayName}} отключил вас от конференции.",
|
||||
"liveStreaming": "Трансляция",
|
||||
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "Невозможно пока активна запись",
|
||||
"liveStreamingDisabledForGuestTooltip": "Гости не могут начать трансляцию",
|
||||
@@ -482,6 +482,7 @@
|
||||
"passwordField": "Введите пароль встречи",
|
||||
"passwordJoinButton": "Присоединиться",
|
||||
"reject": "Отказать",
|
||||
"rejectAll": "Отказать всем",
|
||||
"toggleLabel": "Включить лобби"
|
||||
},
|
||||
"localRecording": {
|
||||
@@ -523,6 +524,7 @@
|
||||
},
|
||||
"me": "я",
|
||||
"notify": {
|
||||
"allowAction": "Разрешить",
|
||||
"OldElectronAPPTitle": "Уязвимость в системе безопасности!",
|
||||
"connectedOneMember": "{{name}} присоединился к конференции",
|
||||
"connectedThreePlusMembers": "{{name}} и {{count}} других пользователей присоединились к конференции",
|
||||
@@ -534,7 +536,7 @@
|
||||
"invitedOneMember": "{{name}} был приглашен",
|
||||
"invitedThreePlusMembers": "Приглашены {{name}} и {{count}} других пользователей(ля)",
|
||||
"invitedTwoMembers": "{{first}} и {{second}} присоединились к конференции",
|
||||
"kickParticipant": "{{kicker}} выгнал {{kicked}}",
|
||||
"kickParticipant": "{{kicker}} отключил {{kicked}}",
|
||||
"me": "Я",
|
||||
"moderator": "Получены права модератора!",
|
||||
"muted": "Вы начали разговор без звука.",
|
||||
@@ -801,7 +803,7 @@
|
||||
"moreOptions": "Больше настроек",
|
||||
"mute": "Микрофон (вкл./выкл.)",
|
||||
"muteEveryone": "Выкл. микрофон у всех",
|
||||
"muteEveryonesVideo": "Выкл. Камеру у всех",
|
||||
"muteEveryonesVideo": "Выкл. камеру у всех",
|
||||
"noAudioSignalTitle": "От вашего микрофона не идет звуковой сигнал!",
|
||||
"noAudioSignalDesc": "Если вы специально не отключали микрофон в системных настройках, подумайте о том, чтобы поменять его.",
|
||||
"noAudioSignalDescSuggestion": "Если вы специально не отключали микрофон в системных настройках, вы можете попробовать использовать следующее устройство:",
|
||||
|
||||
@@ -39,6 +39,20 @@
|
||||
"audioOnly": {
|
||||
"audioOnly": "Low bandwidth"
|
||||
},
|
||||
"breakoutRooms": {
|
||||
"defaultName": "Breakout room #{{index}}",
|
||||
"mainRoom": "Main room",
|
||||
"actions": {
|
||||
"add": "Add breakout room",
|
||||
"autoAssign": "Auto assign to breakout rooms",
|
||||
"close": "Close",
|
||||
"join": "Join",
|
||||
"leaveBreakoutRoom": "Leave breakout room",
|
||||
"more": "More",
|
||||
"remove": "Remove",
|
||||
"sendToBreakoutRoom": "Send participant to:"
|
||||
}
|
||||
},
|
||||
"calendarSync": {
|
||||
"addMeetingURL": "Add a meeting link",
|
||||
"confirmAddLink": "Do you want to add a Jitsi link to this event?",
|
||||
@@ -268,12 +282,12 @@
|
||||
"muteEveryoneStartMuted": "Everyone starts muted from now on",
|
||||
"muteParticipantBody": "You won't be able to unmute them, but they can unmute themselves at any time.",
|
||||
"muteParticipantButton": "Mute",
|
||||
"muteParticipantDialog": "Are you sure you want to mute this participant? You won't be able to unmute them, but they can unmute themselves at any time.",
|
||||
"muteParticipantsVideoDialog": "Are you sure you want to turn off this participant's camera? You won't be able to turn the camera back on, but they can turn it back on at any time.",
|
||||
"muteParticipantTitle": "Mute this participant?",
|
||||
"muteParticipantsVideoDialogModerationOn": "Are you sure you want to turn off this participant's camera? You won't be able to turn the camera back on and neither will they.",
|
||||
"muteParticipantsVideoButton": "Stop video",
|
||||
"muteParticipantsVideoTitle": "Disable camera of this participant?",
|
||||
"muteParticipantsVideoBody": "You won't be able to turn the camera back on, but they can turn it back on at any time.",
|
||||
"muteParticipantsVideoBodyModerationOn": "You won't be able to turn the camera back on and neither will they.",
|
||||
"noDropboxToken": "No valid Dropbox token",
|
||||
"Ok": "OK",
|
||||
"password": "Password",
|
||||
@@ -501,6 +515,7 @@
|
||||
"expandedPending": "The live streaming is being started...",
|
||||
"failedToStart": "Live Streaming failed to start",
|
||||
"getStreamKeyManually": "We weren’t able to fetch any live streams. Try getting your live stream key from YouTube.",
|
||||
"inProgress": "Recording or live streaming in progress",
|
||||
"invalidStreamKey": "Live stream key may be incorrect.",
|
||||
"off": "Live Streaming stopped",
|
||||
"offBy": "{{name}} stopped the live streaming",
|
||||
@@ -508,6 +523,7 @@
|
||||
"onBy": "{{name}} started the live streaming",
|
||||
"pending": "Starting Live Stream...",
|
||||
"serviceName": "Live Streaming service",
|
||||
"sessionAlreadyActive": "This session is already being recorded or live streamed.",
|
||||
"signedInAs": "You are currently signed in as:",
|
||||
"signIn": "Sign in with Google",
|
||||
"signInCTA": "Sign in or enter your live stream key from YouTube.",
|
||||
@@ -568,9 +584,9 @@
|
||||
"moderator": "You're now a moderator",
|
||||
"muted": "You have started the conversation muted.",
|
||||
"mutedTitle": "You're muted!",
|
||||
"mutedRemotelyTitle": "You've been muted by {{moderator}}",
|
||||
"mutedRemotelyTitle": "You've been muted by {{participantDisplayName}}",
|
||||
"mutedRemotelyDescription": "You can always unmute when you're ready to speak. Mute back when you're done to keep noise away from the meeting.",
|
||||
"videoMutedRemotelyTitle": "Your video has been turned off by {{moderator}}",
|
||||
"videoMutedRemotelyTitle": "Your video has been turned off by {{participantDisplayName}}",
|
||||
"videoMutedRemotelyDescription": "You can always turn it on again.",
|
||||
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) removed by another participant",
|
||||
"passwordSetRemotely": "$t(lockRoomPasswordUppercase) set by another participant",
|
||||
@@ -620,6 +636,8 @@
|
||||
"blockEveryoneMicCamera": "Block everyone's mic and camera",
|
||||
"invite": "Invite Someone",
|
||||
"askUnmute": "Ask to unmute",
|
||||
"moreModerationActions": "More moderation options",
|
||||
"moreParticipantOptions": "More participant options",
|
||||
"mute": "Mute",
|
||||
"muteAll": "Mute all",
|
||||
"muteEveryoneElse": "Mute everyone else",
|
||||
@@ -627,11 +645,13 @@
|
||||
"stopVideo": "Stop video",
|
||||
"unblockEveryoneMicCamera": "Unblock everyone's mic and camera",
|
||||
"videoModeration": "Start their video"
|
||||
}
|
||||
},
|
||||
"search": "Search participants"
|
||||
},
|
||||
"passwordSetRemotely": "Set by another participant",
|
||||
"passwordDigitsOnly": "Up to {{number}} digits",
|
||||
"polls": {
|
||||
"by": "By {{ name }}",
|
||||
"create": {
|
||||
"addOption": "Add option",
|
||||
"answerPlaceholder": "Option {{index}}",
|
||||
@@ -757,6 +777,7 @@
|
||||
"expandedPending": "Recording is being started...",
|
||||
"failedToStart": "Recording failed to start",
|
||||
"fileSharingdescription": "Share recording with meeting participants",
|
||||
"inProgress": "Recording or live streaming in progress",
|
||||
"linkGenerated": "We have generated a link to your recording.",
|
||||
"live": "LIVE",
|
||||
"loggedIn": "Logged in as {{userName}}",
|
||||
@@ -769,6 +790,7 @@
|
||||
"serviceDescription": "Your recording will be saved by the recording service",
|
||||
"serviceDescriptionCloud": "Cloud recording",
|
||||
"serviceName": "Recording service",
|
||||
"sessionAlreadyActive": "This session is already being recorded or live streamed.",
|
||||
"signIn": "Sign in",
|
||||
"signOut": "Sign out",
|
||||
"unavailable": "Oops! The {{serviceName}} is currently unavailable. We're working on resolving the issue. Please try again later.",
|
||||
@@ -855,7 +877,14 @@
|
||||
"name": "Name",
|
||||
"seconds": "{{count}}s",
|
||||
"speakerStats": "Speaker Stats",
|
||||
"speakerTime": "Speaker Time"
|
||||
"speakerTime": "Speaker Time",
|
||||
"happy": "Happy",
|
||||
"neutral": "Neutral",
|
||||
"sad": "Sad",
|
||||
"surprised": "Surprised",
|
||||
"angry": "Angry",
|
||||
"fearful": "Fearful",
|
||||
"disgusted": "Disgusted"
|
||||
},
|
||||
"startupoverlay": {
|
||||
"policyText": " ",
|
||||
@@ -872,6 +901,7 @@
|
||||
"audioOnly": "Toggle audio only",
|
||||
"audioRoute": "Select the sound device",
|
||||
"boo": "Boo",
|
||||
"breakoutRoom": "Join/leave breakout room",
|
||||
"callQuality": "Manage video quality",
|
||||
"cc": "Toggle subtitles",
|
||||
"chat": "Open / Close chat",
|
||||
@@ -955,7 +985,9 @@
|
||||
"hangup": "Leave the meeting",
|
||||
"help": "Help",
|
||||
"invite": "Invite people",
|
||||
"joinBreakoutRoom": "Join breakout room",
|
||||
"laugh": "Laugh",
|
||||
"leaveBreakoutRoom": "Leave breakout room",
|
||||
"like": "Thumbs Up",
|
||||
"lobbyButtonDisable": "Disable lobby mode",
|
||||
"lobbyButtonEnable": "Enable lobby mode",
|
||||
@@ -1133,6 +1165,12 @@
|
||||
"button": "Invite others",
|
||||
"youAreAlone": "You are the only one in the meeting"
|
||||
},
|
||||
"termsView": {
|
||||
"header": "Terms"
|
||||
},
|
||||
"privacyView": {
|
||||
"header": "Privacy"
|
||||
},
|
||||
"helpView": {
|
||||
"header": "Help center"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import Logger from 'jitsi-meet-logger';
|
||||
import Logger from '@jitsi/logger';
|
||||
|
||||
import {
|
||||
createApiEvent,
|
||||
@@ -37,7 +37,8 @@ import {
|
||||
kickParticipant,
|
||||
raiseHand,
|
||||
isParticipantModerator,
|
||||
isLocalParticipantModerator
|
||||
isLocalParticipantModerator,
|
||||
hasRaisedHand
|
||||
} from '../../react/features/base/participants';
|
||||
import { updateSettings } from '../../react/features/base/settings';
|
||||
import { isToggleCameraEnabled, toggleCamera } from '../../react/features/base/tracks';
|
||||
@@ -51,7 +52,7 @@ import {
|
||||
processExternalDeviceRequest
|
||||
} from '../../react/features/device-selection/functions';
|
||||
import { isEnabled as isDropboxEnabled } from '../../react/features/dropbox';
|
||||
import { toggleE2EE } from '../../react/features/e2ee/actions';
|
||||
import { setMediaEncryptionKey, toggleE2EE } from '../../react/features/e2ee/actions';
|
||||
import { setVolume } from '../../react/features/filmstrip';
|
||||
import { invite } from '../../react/features/invite';
|
||||
import {
|
||||
@@ -61,7 +62,7 @@ import {
|
||||
captureLargeVideoScreenshot,
|
||||
resizeLargeVideo
|
||||
} from '../../react/features/large-video/actions.web';
|
||||
import { toggleLobbyMode } from '../../react/features/lobby/actions';
|
||||
import { toggleLobbyMode, setKnockingParticipantApproval } from '../../react/features/lobby/actions';
|
||||
import { isForceMuted } from '../../react/features/participants-pane/functions';
|
||||
import { RECORDING_TYPES } from '../../react/features/recording/constants';
|
||||
import { getActiveSession } from '../../react/features/recording/functions';
|
||||
@@ -113,6 +114,9 @@ let videoAvailable = true;
|
||||
*/
|
||||
function initCommands() {
|
||||
commands = {
|
||||
'answer-knocking-participant': (id, approved) => {
|
||||
APP.store.dispatch(setKnockingParticipantApproval(id, approved));
|
||||
},
|
||||
'approve-video': participantId => {
|
||||
if (!isLocalParticipantModerator(APP.store.getState())) {
|
||||
return;
|
||||
@@ -278,7 +282,7 @@ function initCommands() {
|
||||
if (!localParticipant) {
|
||||
return;
|
||||
}
|
||||
const { raisedHand } = localParticipant;
|
||||
const raisedHand = hasRaisedHand(localParticipant);
|
||||
|
||||
sendAnalytics(createApiEvent('raise-hand.toggled'));
|
||||
APP.store.dispatch(raiseHand(!raisedHand));
|
||||
@@ -360,6 +364,9 @@ function initCommands() {
|
||||
logger.debug('Toggle E2EE key command received');
|
||||
APP.store.dispatch(toggleE2EE(enabled));
|
||||
},
|
||||
'set-media-encryption-key': keyInfo => {
|
||||
APP.store.dispatch(setMediaEncryptionKey(JSON.parse(keyInfo)));
|
||||
},
|
||||
'set-video-quality': frameHeight => {
|
||||
logger.debug('Set video quality command received');
|
||||
sendAnalytics(createApiEvent('set.video.quality'));
|
||||
@@ -1458,6 +1465,33 @@ class API {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that the current recording link is
|
||||
* available.
|
||||
*
|
||||
* @param {string} link - The recording download link.
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyRecordingLinkAvailable(link: string) {
|
||||
this._sendEvent({
|
||||
name: 'recording-link-available',
|
||||
link
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that a participant is knocking in the lobby.
|
||||
*
|
||||
* @param {Object} participant - Participant data such as id and name.
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyKnockingParticipant(participant: Object) {
|
||||
this._sendEvent({
|
||||
name: 'knocking-participant',
|
||||
participant
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that an error occured.
|
||||
*
|
||||
|
||||
@@ -12,6 +12,6 @@ import { parseURLParams } from '../../react/features/base/util/parseURLParams';
|
||||
export const API_ID = parseURLParams(window.location).jitsi_meet_external_api_id;
|
||||
|
||||
/**
|
||||
* The payload name for the datachannel/endpoint text message event
|
||||
* The payload name for the datachannel/endpoint text message event.
|
||||
*/
|
||||
export const ENDPOINT_TEXT_MESSAGE_NAME = 'endpoint-text-message';
|
||||
|
||||
50
modules/API/external/external_api.js
vendored
50
modules/API/external/external_api.js
vendored
@@ -24,9 +24,10 @@ const ALWAYS_ON_TOP_FILENAMES = [
|
||||
|
||||
/**
|
||||
* Maps the names of the commands expected by the API with the name of the
|
||||
* commands expected by jitsi-meet
|
||||
* commands expected by jitsi-meet.
|
||||
*/
|
||||
const commands = {
|
||||
answerKnockingParticipant: 'answer-knocking-participant',
|
||||
approveVideo: 'approve-video',
|
||||
askToUnmute: 'ask-to-unmute',
|
||||
avatarUrl: 'avatar-url',
|
||||
@@ -49,6 +50,7 @@ const commands = {
|
||||
sendTones: 'send-tones',
|
||||
setFollowMe: 'set-follow-me',
|
||||
setLargeVideoParticipant: 'set-large-video-participant',
|
||||
setMediaEncryptionKey: 'set-media-encryption-key',
|
||||
setParticipantVolume: 'set-participant-volume',
|
||||
setTileView: 'set-tile-view',
|
||||
setVideoQuality: 'set-video-quality',
|
||||
@@ -62,6 +64,7 @@ const commands = {
|
||||
toggleCamera: 'toggle-camera',
|
||||
toggleCameraMirror: 'toggle-camera-mirror',
|
||||
toggleChat: 'toggle-chat',
|
||||
toggleE2EE: 'toggle-e2ee',
|
||||
toggleFilmStrip: 'toggle-film-strip',
|
||||
toggleModeration: 'toggle-moderation',
|
||||
toggleRaiseHand: 'toggle-raise-hand',
|
||||
@@ -74,7 +77,7 @@ const commands = {
|
||||
|
||||
/**
|
||||
* Maps the names of the events expected by the API with the name of the
|
||||
* events expected by jitsi-meet
|
||||
* events expected by jitsi-meet.
|
||||
*/
|
||||
const events = {
|
||||
'avatar-changed': 'avatarChanged',
|
||||
@@ -94,6 +97,7 @@ const events = {
|
||||
'feedback-prompt-displayed': 'feedbackPromptDisplayed',
|
||||
'filmstrip-display-changed': 'filmstripDisplayChanged',
|
||||
'incoming-message': 'incomingMessage',
|
||||
'knocking-participant': 'knockingParticipant',
|
||||
'log': 'log',
|
||||
'mic-error': 'micError',
|
||||
'moderation-participant-approved': 'moderationParticipantApproved',
|
||||
@@ -110,6 +114,7 @@ const events = {
|
||||
'password-required': 'passwordRequired',
|
||||
'proxy-connection-event': 'proxyConnectionEvent',
|
||||
'raise-hand-updated': 'raiseHandUpdated',
|
||||
'recording-link-available': 'recordingLinkAvailable',
|
||||
'recording-status-changed': 'recordingStatusChanged',
|
||||
'video-ready-to-close': 'readyToClose',
|
||||
'video-conference-joined': 'videoConferenceJoined',
|
||||
@@ -126,7 +131,8 @@ const events = {
|
||||
};
|
||||
|
||||
/**
|
||||
* Last id of api object
|
||||
* Last id of api object.
|
||||
*
|
||||
* @type {number}
|
||||
*/
|
||||
let id = 0;
|
||||
@@ -384,7 +390,7 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
}
|
||||
|
||||
return ALWAYS_ON_TOP_FILENAMES.map(
|
||||
filename => (new URL(filename, baseURL)).href
|
||||
filename => new URL(filename, baseURL).href
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1181,6 +1187,40 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
* @returns {void}
|
||||
*/
|
||||
stopRecording(mode) {
|
||||
this.executeCommand('startRecording', mode);
|
||||
this.executeCommand('stopRecording', mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets e2ee enabled/disabled.
|
||||
*
|
||||
* @param {boolean} enabled - The new value for e2ee enabled.
|
||||
* @returns {void}
|
||||
*/
|
||||
toggleE2EE(enabled) {
|
||||
this.executeCommand('toggleE2EE', enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the key and keyIndex for e2ee.
|
||||
*
|
||||
* @param {Object} keyInfo - Json containing key information.
|
||||
* @param {CryptoKey} [keyInfo.encryptionKey] - The encryption key.
|
||||
* @param {number} [keyInfo.index] - The index of the encryption key.
|
||||
* @returns {void}
|
||||
*/
|
||||
async setMediaEncryptionKey(keyInfo) {
|
||||
const { key, index } = keyInfo;
|
||||
|
||||
if (key) {
|
||||
const exportedKey = await crypto.subtle.exportKey('raw', key);
|
||||
|
||||
this.executeCommand('setMediaEncryptionKey', JSON.stringify({
|
||||
exportedKey: Array.from(new Uint8Array(exportedKey)),
|
||||
index }));
|
||||
} else {
|
||||
this.executeCommand('setMediaEncryptionKey', JSON.stringify({
|
||||
exportedKey: false,
|
||||
index }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
2
modules/API/external/functions.js
vendored
2
modules/API/external/functions.js
vendored
@@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import Logger from 'jitsi-meet-logger';
|
||||
import Logger from '@jitsi/logger';
|
||||
|
||||
const logger = Logger.getLogger(__filename);
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
|
||||
const UI = {};
|
||||
|
||||
import Logger from '@jitsi/logger';
|
||||
import EventEmitter from 'events';
|
||||
import Logger from 'jitsi-meet-logger';
|
||||
|
||||
import { isMobileBrowser } from '../../react/features/base/environment/utils';
|
||||
import { setColorAlpha } from '../../react/features/base/util';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import Logger from 'jitsi-meet-logger';
|
||||
import Logger from '@jitsi/logger';
|
||||
|
||||
import { openConnection } from '../../../connection';
|
||||
import {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* global $, APP */
|
||||
/* global APP */
|
||||
|
||||
import {
|
||||
NOTIFICATION_TIMEOUT,
|
||||
@@ -8,44 +8,6 @@ import {
|
||||
} from '../../../react/features/notifications';
|
||||
|
||||
const messageHandler = {
|
||||
OK: 'dialog.OK',
|
||||
CANCEL: 'dialog.Cancel',
|
||||
|
||||
/**
|
||||
* Returns the formatted title string.
|
||||
*
|
||||
* @return the title string formatted as a div.
|
||||
*/
|
||||
_getFormattedTitleString(titleKey) {
|
||||
const $titleString = $('<h2>');
|
||||
|
||||
$titleString.addClass('aui-dialog2-header-main');
|
||||
$titleString.attr('data-i18n', titleKey);
|
||||
|
||||
return $('<div>').append($titleString)
|
||||
.html();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the dialog css classes.
|
||||
*
|
||||
* @return the dialog css classes
|
||||
*/
|
||||
_getDialogClasses(size = 'small') {
|
||||
return {
|
||||
box: '',
|
||||
form: '',
|
||||
prompt: `dialog aui-layer aui-dialog2 aui-dialog2-${size}`,
|
||||
close: 'aui-hide',
|
||||
fade: 'aui-blanket',
|
||||
button: 'button-control',
|
||||
message: 'aui-dialog2-content',
|
||||
buttons: 'aui-dialog2-footer',
|
||||
defaultButton: 'button-control_primary',
|
||||
title: 'aui-dialog2-header'
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens new popup window for given <tt>url</tt> centered over current
|
||||
* window.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* global $, APP */
|
||||
/* eslint-disable no-unused-vars */
|
||||
import Logger from 'jitsi-meet-logger';
|
||||
import Logger from '@jitsi/logger';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { I18nextProvider } from 'react-i18next';
|
||||
@@ -8,6 +8,7 @@ import { Provider } from 'react-redux';
|
||||
|
||||
import { createScreenSharingIssueEvent, sendAnalytics } from '../../../react/features/analytics';
|
||||
import { Avatar } from '../../../react/features/base/avatar';
|
||||
import theme from '../../../react/features/base/components/themes/participantsPaneTheme.json';
|
||||
import { i18next } from '../../../react/features/base/i18n';
|
||||
import {
|
||||
JitsiParticipantConnectionStatus
|
||||
@@ -20,7 +21,6 @@ import {
|
||||
updateKnownLargeVideoResolution
|
||||
} from '../../../react/features/large-video/actions';
|
||||
import { getParticipantsPaneOpen } from '../../../react/features/participants-pane/functions';
|
||||
import theme from '../../../react/features/participants-pane/theme.json';
|
||||
import { PresenceLabel } from '../../../react/features/presence-status';
|
||||
import { shouldDisplayTileView } from '../../../react/features/video-layout';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
@@ -495,6 +495,10 @@ export class VideoContainer extends LargeContainer {
|
||||
|
||||
stream.attach(this.$video[0]);
|
||||
|
||||
// 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.$video[0].play();
|
||||
|
||||
const flipX = stream.isLocal() && this.localFlipX && !this.isScreenSharing();
|
||||
|
||||
this.$video.css({
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* global APP */
|
||||
|
||||
import Logger from 'jitsi-meet-logger';
|
||||
import Logger from '@jitsi/logger';
|
||||
|
||||
import { MEDIA_TYPE, VIDEO_TYPE } from '../../../react/features/base/media';
|
||||
import {
|
||||
|
||||
@@ -8,7 +8,8 @@ import {
|
||||
import {
|
||||
getUserSelectedCameraDeviceId,
|
||||
getUserSelectedMicDeviceId,
|
||||
getUserSelectedOutputDeviceId
|
||||
getUserSelectedOutputDeviceId,
|
||||
updateSettings
|
||||
} from '../../react/features/base/settings';
|
||||
|
||||
/**
|
||||
@@ -51,15 +52,19 @@ function getNewAudioOutputDevice(newDevices) {
|
||||
* list of available devices has been changed.
|
||||
* @param {MediaDeviceInfo[]} newDevices
|
||||
* @param {JitsiLocalTrack} localAudio
|
||||
* @param {boolean} newLabel
|
||||
* @returns {string|undefined} - ID of new microphone device to use, undefined
|
||||
* if audio input device should not be changed.
|
||||
*/
|
||||
function getNewAudioInputDevice(newDevices, localAudio) {
|
||||
function getNewAudioInputDevice(newDevices, localAudio, newLabel) {
|
||||
const availableAudioInputDevices = newDevices.filter(
|
||||
d => d.kind === 'audioinput');
|
||||
const selectedAudioInputDeviceId = getUserSelectedMicDeviceId(APP.store.getState());
|
||||
const selectedAudioInputDevice = availableAudioInputDevices.find(
|
||||
d => d.deviceId === selectedAudioInputDeviceId);
|
||||
const localAudioDeviceId = localAudio?.getDeviceId();
|
||||
const localAudioDevice = availableAudioInputDevices.find(
|
||||
d => d.deviceId === localAudioDeviceId);
|
||||
|
||||
// Here we handle case when no device was initially plugged, but
|
||||
// then it's connected OR new device was connected when previous
|
||||
@@ -75,12 +80,22 @@ function getNewAudioInputDevice(newDevices, localAudio) {
|
||||
return availableAudioInputDevices[0].deviceId;
|
||||
}
|
||||
} else if (selectedAudioInputDevice
|
||||
&& selectedAudioInputDeviceId !== localAudio.getDeviceId()) {
|
||||
&& selectedAudioInputDeviceId !== localAudioDeviceId) {
|
||||
|
||||
// And here we handle case when we already have some device working,
|
||||
// but we plug-in a "preferred" (previously selected in settings, stored
|
||||
// in local storage) device.
|
||||
return selectedAudioInputDeviceId;
|
||||
if (newLabel) {
|
||||
// If a Firefox user with manual permission prompt chose a different
|
||||
// device from what we have stored as the preferred device we accept
|
||||
// and store that as the new preferred device.
|
||||
APP.store.dispatch(updateSettings({
|
||||
userSelectedMicDeviceId: localAudioDeviceId,
|
||||
userSelectedMicDeviceLabel: localAudioDevice.label
|
||||
}));
|
||||
} else {
|
||||
// And here we handle case when we already have some device working,
|
||||
// but we plug-in a "preferred" (previously selected in settings, stored
|
||||
// in local storage) device.
|
||||
return selectedAudioInputDeviceId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,15 +104,19 @@ function getNewAudioInputDevice(newDevices, localAudio) {
|
||||
* list of available devices has been changed.
|
||||
* @param {MediaDeviceInfo[]} newDevices
|
||||
* @param {JitsiLocalTrack} localVideo
|
||||
* @param {boolean} newLabel
|
||||
* @returns {string|undefined} - ID of new camera device to use, undefined
|
||||
* if video input device should not be changed.
|
||||
*/
|
||||
function getNewVideoInputDevice(newDevices, localVideo) {
|
||||
function getNewVideoInputDevice(newDevices, localVideo, newLabel) {
|
||||
const availableVideoInputDevices = newDevices.filter(
|
||||
d => d.kind === 'videoinput');
|
||||
const selectedVideoInputDeviceId = getUserSelectedCameraDeviceId(APP.store.getState());
|
||||
const selectedVideoInputDevice = availableVideoInputDevices.find(
|
||||
d => d.deviceId === selectedVideoInputDeviceId);
|
||||
const localVideoDeviceId = localVideo?.getDeviceId();
|
||||
const localVideoDevice = availableVideoInputDevices.find(
|
||||
d => d.deviceId === localVideoDeviceId);
|
||||
|
||||
// Here we handle case when no video input device was initially plugged,
|
||||
// but then device is connected OR new device was connected when
|
||||
@@ -113,11 +132,22 @@ function getNewVideoInputDevice(newDevices, localVideo) {
|
||||
return availableVideoInputDevices[0].deviceId;
|
||||
}
|
||||
} else if (selectedVideoInputDevice
|
||||
&& selectedVideoInputDeviceId !== localVideo.getDeviceId()) {
|
||||
// And here we handle case when we already have some device working,
|
||||
// but we plug-in a "preferred" (previously selected in settings, stored
|
||||
// in local storage) device.
|
||||
return selectedVideoInputDeviceId;
|
||||
&& selectedVideoInputDeviceId !== localVideoDeviceId) {
|
||||
|
||||
if (newLabel) {
|
||||
// If a Firefox user with manual permission prompt chose a different
|
||||
// device from what we have stored as the preferred device we accept
|
||||
// and store that as the new preferred device.
|
||||
APP.store.dispatch(updateSettings({
|
||||
userSelectedCameraDeviceId: localVideoDeviceId,
|
||||
userSelectedCameraDeviceLabel: localVideoDevice.label
|
||||
}));
|
||||
} else {
|
||||
// And here we handle case when we already have some device working,
|
||||
// but we plug-in a "preferred" (previously selected in settings, stored
|
||||
// in local storage) device.
|
||||
return selectedVideoInputDeviceId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,14 +169,42 @@ export default {
|
||||
newDevices,
|
||||
isSharingScreen,
|
||||
localVideo,
|
||||
localAudio) {
|
||||
localAudio,
|
||||
newLabels) {
|
||||
return {
|
||||
audioinput: getNewAudioInputDevice(newDevices, localAudio),
|
||||
videoinput: isSharingScreen ? undefined : getNewVideoInputDevice(newDevices, localVideo),
|
||||
audioinput: getNewAudioInputDevice(newDevices, localAudio, newLabels),
|
||||
videoinput: isSharingScreen ? undefined : getNewVideoInputDevice(newDevices, localVideo, newLabels),
|
||||
audiooutput: getNewAudioOutputDevice(newDevices)
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Checks if the only difference between an object of known devices compared
|
||||
* to an array of new devices are only the labels for the devices.
|
||||
* @param {Object} oldDevices
|
||||
* @param {MediaDeviceInfo[]} newDevices
|
||||
* @returns {boolean}
|
||||
*/
|
||||
newDeviceListAddedLabelsOnly(oldDevices, newDevices) {
|
||||
const oldDevicesFlattend = oldDevices.audioInput.concat(oldDevices.audioOutput).concat(oldDevices.videoInput);
|
||||
|
||||
if (oldDevicesFlattend.length !== newDevices.length) {
|
||||
return false;
|
||||
}
|
||||
oldDevicesFlattend.forEach(oldDevice => {
|
||||
if (oldDevice.label !== '') {
|
||||
return false;
|
||||
}
|
||||
const newDevice = newDevices.find(nd => nd.deviceId === oldDevice.deviceId);
|
||||
|
||||
if (!newDevice || newDevice.label === '') {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Tries to create new local tracks for new devices obtained after device
|
||||
* list changed. Shows error dialog in case of failures.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/* global APP */
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
import Logger from 'jitsi-meet-logger';
|
||||
import Logger from '@jitsi/logger';
|
||||
|
||||
import {
|
||||
ACTION_SHORTCUT_PRESSED as PRESSED,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
const logger = require('@jitsi/logger').getLogger(__filename);
|
||||
|
||||
/**
|
||||
* Manages a queue of functions where the current function in progress will
|
||||
|
||||
13510
package-lock.json
generated
13510
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
47
package.json
47
package.json
@@ -35,6 +35,8 @@
|
||||
"@atlaskit/tooltip": "17.1.2",
|
||||
"@hapi/bourne": "2.0.0",
|
||||
"@jitsi/js-utils": "2.0.0",
|
||||
"@jitsi/logger": "2.0.0",
|
||||
"@jitsi/rtcstats": "9.0.1",
|
||||
"@material-ui/core": "4.11.3",
|
||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.3.tgz",
|
||||
"@microsoft/microsoft-graph-client": "1.1.0",
|
||||
@@ -43,6 +45,8 @@
|
||||
"@react-native-community/netinfo": "4.1.5",
|
||||
"@react-native-community/slider": "3.0.3",
|
||||
"@react-native-masked-view/masked-view": "0.2.6",
|
||||
"@react-navigation/bottom-tabs": "5.11.15",
|
||||
"@react-navigation/drawer": "5.12.9",
|
||||
"@react-navigation/material-top-tabs": "5.3.19",
|
||||
"@react-navigation/native": "5.9.8",
|
||||
"@react-navigation/stack": "5.14.9",
|
||||
@@ -54,18 +58,18 @@
|
||||
"clipboard-copy": "4.0.1",
|
||||
"clsx": "1.1.1",
|
||||
"dropbox": "10.7.0",
|
||||
"face-api.js": "0.22.2",
|
||||
"focus-visible": "5.1.0",
|
||||
"i18n-iso-countries": "6.8.0",
|
||||
"i18next": "17.0.6",
|
||||
"i18next-browser-languagedetector": "3.0.1",
|
||||
"i18next-xhr-backend": "3.0.0",
|
||||
"image-capture": "0.4.0",
|
||||
"jitsi-meet-logger": "github:jitsi/jitsi-meet-logger#v1.0.0",
|
||||
"jquery": "3.5.1",
|
||||
"jquery-i18next": "1.2.1",
|
||||
"js-md5": "0.6.1",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#42c675249aeef632aaf169e0544eeba240f7f962",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#bdfbb82087ce2a9336157da35e739fb4b250ed66",
|
||||
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.1",
|
||||
@@ -87,6 +91,7 @@
|
||||
"react-native-default-preference": "1.4.2",
|
||||
"react-native-device-info": "8.0.0",
|
||||
"react-native-gesture-handler": "1.10.3",
|
||||
"react-native-get-random-values": "1.7.0",
|
||||
"react-native-immersive": "2.0.0",
|
||||
"react-native-keep-awake": "4.0.0",
|
||||
"react-native-paper": "4.8.1",
|
||||
@@ -114,39 +119,33 @@
|
||||
"redux-thunk": "2.2.0",
|
||||
"resemblejs": "4.0.0",
|
||||
"rnnoise-wasm": "github:jitsi/rnnoise-wasm#566a16885897704d6e6d67a1d5ac5d39781db2af",
|
||||
"rtcstats": "github:jitsi/rtcstats#v8.1.0",
|
||||
"styled-components": "3.4.9",
|
||||
"util": "0.12.1",
|
||||
"uuid": "3.1.0",
|
||||
"uuid": "8.3.2",
|
||||
"wasm-check": "2.0.1",
|
||||
"windows-iana": "^3.1.0",
|
||||
"zxcvbn": "4.4.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.9.0",
|
||||
"@babel/plugin-proposal-class-properties": "7.1.0",
|
||||
"@babel/plugin-proposal-export-default-from": "7.0.0",
|
||||
"@babel/plugin-proposal-export-namespace-from": "7.0.0",
|
||||
"@babel/plugin-proposal-nullish-coalescing-operator": "7.4.4",
|
||||
"@babel/plugin-proposal-optional-chaining": "7.2.0",
|
||||
"@babel/plugin-transform-flow-strip-types": "7.0.0",
|
||||
"@babel/preset-env": "7.1.0",
|
||||
"@babel/preset-flow": "7.0.0",
|
||||
"@babel/preset-react": "7.0.0",
|
||||
"@babel/runtime": "7.15.3",
|
||||
"babel-eslint": "10.0.1",
|
||||
"babel-loader": "8.0.4",
|
||||
"@babel/core": "7.16.0",
|
||||
"@babel/eslint-parser": "7.16.0",
|
||||
"@babel/plugin-proposal-export-default-from": "7.16.0",
|
||||
"@babel/preset-env": "7.16.0",
|
||||
"@babel/preset-flow": "7.16.0",
|
||||
"@babel/preset-react": "7.16.0",
|
||||
"@babel/runtime": "7.16.0",
|
||||
"@jitsi/eslint-config": "4.0.0",
|
||||
"babel-loader": "8.2.3",
|
||||
"babel-plugin-optional-require": "0.3.1",
|
||||
"circular-dependency-plugin": "5.2.0",
|
||||
"clean-css-cli": "4.3.0",
|
||||
"css-loader": "3.6.0",
|
||||
"eslint": "5.6.1",
|
||||
"eslint-config-jitsi": "github:jitsi/eslint-config-jitsi#2.0.0",
|
||||
"eslint-plugin-flowtype": "2.50.3",
|
||||
"eslint-plugin-import": "2.20.2",
|
||||
"eslint-plugin-jsdoc": "3.8.0",
|
||||
"eslint-plugin-react": "7.11.1",
|
||||
"eslint-plugin-react-native": "3.3.0",
|
||||
"eslint": "8.1.0",
|
||||
"eslint-plugin-flowtype": "8.0.3",
|
||||
"eslint-plugin-import": "2.25.2",
|
||||
"eslint-plugin-jsdoc": "37.0.3",
|
||||
"eslint-plugin-react": "7.26.1",
|
||||
"eslint-plugin-react-native": "3.11.0",
|
||||
"expose-loader": "3.0.0",
|
||||
"flow-bin": "0.104.0",
|
||||
"imports-loader": "0.7.1",
|
||||
|
||||
11
patches/eslint-plugin-flowtype+8.0.3.patch
Normal file
11
patches/eslint-plugin-flowtype+8.0.3.patch
Normal file
@@ -0,0 +1,11 @@
|
||||
diff --git a/node_modules/eslint-plugin-flowtype/dist/configs/recommended.json b/node_modules/eslint-plugin-flowtype/dist/configs/recommended.json
|
||||
index 90a0d69..2ad7d68 100644
|
||||
--- a/node_modules/eslint-plugin-flowtype/dist/configs/recommended.json
|
||||
+++ b/node_modules/eslint-plugin-flowtype/dist/configs/recommended.json
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
- "parser": "@babel/eslint",
|
||||
+ "parser": "@babel/eslint-parser",
|
||||
"parserOptions": {
|
||||
"babelOptions": {
|
||||
"plugins": [
|
||||
@@ -1,9 +1,18 @@
|
||||
module.exports = {
|
||||
'extends': [
|
||||
'../.eslintrc.js',
|
||||
'eslint-config-jitsi/flow',
|
||||
'eslint-config-jitsi/jsdoc',
|
||||
'eslint-config-jitsi/react',
|
||||
'@jitsi/eslint-config/flow',
|
||||
'@jitsi/eslint-config/jsdoc',
|
||||
'@jitsi/eslint-config/react',
|
||||
'.eslintrc-react-native.js'
|
||||
]
|
||||
],
|
||||
'rules': {
|
||||
// XXX remove this eventually.
|
||||
'react/jsx-indent-props': 0
|
||||
},
|
||||
'settings': {
|
||||
'react': {
|
||||
'version': 'detect'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -33,7 +33,7 @@ type State = {
|
||||
* Represents the always on top page.
|
||||
*
|
||||
* @class AlwaysOnTop
|
||||
* @extends Component
|
||||
* @augments Component
|
||||
*/
|
||||
export default class AlwaysOnTop extends Component<*, State> {
|
||||
_hovered: boolean;
|
||||
|
||||
@@ -30,7 +30,7 @@ type Props = {
|
||||
/**
|
||||
* Represents the toolbar in the Always On Top window.
|
||||
*
|
||||
* @extends Component
|
||||
* @augments Component
|
||||
*/
|
||||
export default class Toolbar extends Component<Props> {
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
* The constant for the event type 'track'.
|
||||
* TODO: keep these constants in a single place. Can we import them from
|
||||
* lib-jitsi-meet's AnalyticsEvents somehow?
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
const TYPE_TRACK = 'track';
|
||||
@@ -10,6 +11,7 @@ const TYPE_TRACK = 'track';
|
||||
* The constant for the event type 'UI' (User Interaction).
|
||||
* TODO: keep these constants in a single place. Can we import them from
|
||||
* lib-jitsi-meet's AnalyticsEvents somehow?
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
const TYPE_UI = 'ui';
|
||||
@@ -143,7 +145,6 @@ export function createCalendarClickedEvent(eventName, attributes = {}) {
|
||||
export function createCalendarSelectedEvent(attributes = {}) {
|
||||
return {
|
||||
action: 'selected',
|
||||
actionSubject: 'calendar.selected',
|
||||
attributes,
|
||||
source: 'calendar',
|
||||
type: TYPE_UI
|
||||
@@ -159,8 +160,8 @@ export function createCalendarSelectedEvent(attributes = {}) {
|
||||
*/
|
||||
export function createCalendarConnectedEvent(attributes = {}) {
|
||||
return {
|
||||
action: 'calendar.connected',
|
||||
actionSubject: 'calendar.connected',
|
||||
action: 'connected',
|
||||
actionSubject: 'calendar',
|
||||
attributes
|
||||
};
|
||||
}
|
||||
@@ -212,7 +213,6 @@ export function createChromeExtensionBannerEvent(installPressed, attributes = {}
|
||||
export function createRecentSelectedEvent(attributes = {}) {
|
||||
return {
|
||||
action: 'selected',
|
||||
actionSubject: 'recent.list.selected',
|
||||
attributes,
|
||||
source: 'recent.list',
|
||||
type: TYPE_UI
|
||||
@@ -533,12 +533,11 @@ export function createRejoinedEvent({ url, lastConferenceDuration, timeSinceLeft
|
||||
export function createRemoteMuteConfirmedEvent(participantId, mediaType) {
|
||||
return {
|
||||
action: 'clicked',
|
||||
actionSubject: 'remote.mute.dialog.confirm.button',
|
||||
attributes: {
|
||||
'participant_id': participantId,
|
||||
'media_type': mediaType
|
||||
},
|
||||
source: 'remote.mute.dialog',
|
||||
source: 'remote.mute.button',
|
||||
type: TYPE_UI
|
||||
};
|
||||
}
|
||||
@@ -582,33 +581,22 @@ export function createRTCStatsTraceCloseEvent(closeEvent) {
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event indicating that an action related to video blur
|
||||
* occurred (e.g. It was started or stopped).
|
||||
*
|
||||
* @param {string} action - The action which occurred.
|
||||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export function createVideoBlurEvent(action) {
|
||||
return {
|
||||
action,
|
||||
actionSubject: 'video.blur'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event indicating that an action related to screen sharing
|
||||
* occurred (e.g. It was started or stopped).
|
||||
*
|
||||
* @param {string} action - The action which occurred.
|
||||
* @param {number?} value - The screenshare duration in seconds.
|
||||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export function createScreenSharingEvent(action) {
|
||||
export function createScreenSharingEvent(action, value = null) {
|
||||
return {
|
||||
action,
|
||||
actionSubject: 'screen.sharing'
|
||||
actionSubject: 'screen.sharing',
|
||||
attributes: {
|
||||
value
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -625,26 +613,6 @@ export function createScreenSharingIssueEvent(attributes) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* The local participant failed to send a "selected endpoint" message to the
|
||||
* bridge.
|
||||
*
|
||||
* @param {Error} error - The error which caused the failure.
|
||||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export function createSelectParticipantFailedEvent(error) {
|
||||
const event = {
|
||||
action: 'select.participant.failed'
|
||||
};
|
||||
|
||||
if (error) {
|
||||
event.error = error.toString();
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event associated with the "shared video" feature.
|
||||
*
|
||||
@@ -682,7 +650,6 @@ export function createShortcutEvent(
|
||||
attributes = {}) {
|
||||
return {
|
||||
action,
|
||||
actionSubject: 'keyboard.shortcut',
|
||||
actionSubjectId: shortcut,
|
||||
attributes,
|
||||
source: 'keyboard.shortcut',
|
||||
@@ -828,8 +795,24 @@ export function createToolbarEvent(buttonName, attributes = {}) {
|
||||
export function createReactionMenuEvent(buttonName) {
|
||||
return {
|
||||
action: 'clicked',
|
||||
actionSubject: buttonName,
|
||||
source: 'reaction.button',
|
||||
actionSubject: 'button',
|
||||
source: 'reaction',
|
||||
buttonName,
|
||||
type: TYPE_UI
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event associated with disabling of reaction sounds.
|
||||
*
|
||||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export function createReactionSoundsDisabledEvent() {
|
||||
return {
|
||||
action: 'disabled',
|
||||
actionSubject: 'sounds',
|
||||
source: 'reaction.settings',
|
||||
type: TYPE_UI
|
||||
};
|
||||
}
|
||||
@@ -898,6 +881,6 @@ export function createWelcomePageEvent(action, actionSubject, attributes = {}) {
|
||||
*/
|
||||
export function createScreensharingCaptureTakenEvent() {
|
||||
return {
|
||||
action: 'screen.sharing.capture'
|
||||
action: 'screen.sharing.capture.taken'
|
||||
};
|
||||
}
|
||||
|
||||
@@ -187,7 +187,7 @@ export function initAnalytics({ getState }: { getState: Function }, handlers: Ar
|
||||
permanentProperties.externalApi = typeof API_ID === 'number';
|
||||
|
||||
// Report if we are loaded in iframe
|
||||
permanentProperties.inIframe = _inIframe();
|
||||
permanentProperties.inIframe = inIframe();
|
||||
|
||||
// Report the tenant from the URL.
|
||||
permanentProperties.tenant = tenant || '/';
|
||||
@@ -227,7 +227,7 @@ export function initAnalytics({ getState }: { getState: Function }, handlers: Ar
|
||||
* @returns {boolean} Returns {@code true} if loaded in iframe.
|
||||
* @private
|
||||
*/
|
||||
function _inIframe() {
|
||||
export function inIframe() {
|
||||
if (navigator.product === 'ReactNative') {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/**
|
||||
* Abstract implementation of analytics handler
|
||||
* Abstract implementation of analytics handler.
|
||||
*/
|
||||
export default class AbstractHandler {
|
||||
/**
|
||||
|
||||
@@ -51,7 +51,7 @@ type Props = AbstractAppProps & {
|
||||
/**
|
||||
* Root app {@code Component} on mobile/React Native.
|
||||
*
|
||||
* @extends AbstractApp
|
||||
* @augments AbstractApp
|
||||
*/
|
||||
export class App extends AbstractApp {
|
||||
_init: Promise<*>;
|
||||
|
||||
@@ -17,7 +17,7 @@ import '../reducers';
|
||||
/**
|
||||
* Root app {@code Component} on Web/React.
|
||||
*
|
||||
* @extends AbstractApp
|
||||
* @augments AbstractApp
|
||||
*/
|
||||
export class App extends AbstractApp {
|
||||
/**
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
import { isRoomValid } from '../base/conference';
|
||||
import { toState } from '../base/redux';
|
||||
import { ConferenceNavigationContainer } from '../conference';
|
||||
import { isWelcomePageAppEnabled } from '../welcome';
|
||||
import { BlankPage, WelcomePage } from '../welcome/components';
|
||||
import RootNavigationContainer from '../welcome/components/RootNavigationContainer';
|
||||
|
||||
/**
|
||||
* Determines which route is to be rendered in order to depict a specific Redux
|
||||
@@ -26,27 +25,17 @@ export function _getRouteToRender(stateful) {
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function _getMobileRoute(state) {
|
||||
const route = _getEmptyRoute();
|
||||
const route = {
|
||||
component: null,
|
||||
href: undefined
|
||||
};
|
||||
|
||||
if (isRoomValid(state['features/base/conference'].room)) {
|
||||
route.component = ConferenceNavigationContainer;
|
||||
} else if (isWelcomePageAppEnabled(state)) {
|
||||
route.component = WelcomePage;
|
||||
} else {
|
||||
route.component = BlankPage;
|
||||
route.component = RootNavigationContainer;
|
||||
}
|
||||
|
||||
return Promise.resolve(route);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default {@code Route}.
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
function _getEmptyRoute() {
|
||||
return {
|
||||
component: BlankPage,
|
||||
href: undefined
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,9 +7,7 @@ import { toState } from '../base/redux';
|
||||
import { Conference } from '../conference';
|
||||
import { getDeepLinkingPage } from '../deep-linking';
|
||||
import { UnsupportedDesktopBrowser } from '../unsupported-browser';
|
||||
import { isWelcomePageUserEnabled } from '../welcome';
|
||||
import { BlankPage, WelcomePage } from '../welcome/components';
|
||||
|
||||
import { BlankPage, isWelcomePageUserEnabled, WelcomePage } from '../welcome';
|
||||
|
||||
/**
|
||||
* Determines which route is to be rendered in order to depict a specific Redux
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import {
|
||||
createConnectionEvent,
|
||||
inIframe,
|
||||
sendAnalytics
|
||||
} from '../analytics';
|
||||
import { SET_ROOM } from '../base/conference';
|
||||
@@ -53,6 +54,10 @@ function _connectionEstablished(store, next, action) {
|
||||
// determined by when no one needs them anymore.
|
||||
const { history, location } = window;
|
||||
|
||||
if (inIframe()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (history
|
||||
&& location
|
||||
&& history.length
|
||||
|
||||
@@ -19,6 +19,7 @@ import '../base/sounds/middleware';
|
||||
import '../base/testing/middleware';
|
||||
import '../base/tracks/middleware';
|
||||
import '../base/user-interaction/middleware';
|
||||
import '../breakout-rooms/middleware';
|
||||
import '../calendar-sync/middleware';
|
||||
import '../chat/middleware';
|
||||
import '../conference/middleware';
|
||||
|
||||
@@ -17,5 +17,6 @@ import '../screen-share/middleware';
|
||||
import '../shared-video/middleware';
|
||||
import '../talk-while-muted/middleware';
|
||||
import '../virtual-background/middleware';
|
||||
import '../facial-recognition/middleware';
|
||||
|
||||
import './middlewares.any';
|
||||
|
||||
@@ -26,6 +26,7 @@ import '../base/sounds/reducer';
|
||||
import '../base/testing/reducer';
|
||||
import '../base/tracks/reducer';
|
||||
import '../base/user-interaction/reducer';
|
||||
import '../breakout-rooms/reducer';
|
||||
import '../calendar-sync/reducer';
|
||||
import '../chat/reducer';
|
||||
import '../deep-linking/reducer';
|
||||
@@ -54,4 +55,3 @@ import '../toolbox/reducer';
|
||||
import '../transcribing/reducer';
|
||||
import '../video-layout/reducer';
|
||||
import '../videosipgw/reducer';
|
||||
import '../welcome/reducer';
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import '../base/devices/reducer';
|
||||
import '../e2ee/reducer';
|
||||
import '../facial-recognition/reducer';
|
||||
import '../feedback/reducer';
|
||||
import '../local-recording/reducer';
|
||||
import '../no-audio-signal/reducer';
|
||||
|
||||
@@ -29,7 +29,7 @@ type Props = {
|
||||
/**
|
||||
* Creates a ReactElement responsible for drawing audio levels.
|
||||
*
|
||||
* @extends {Component}
|
||||
* @augments {Component}
|
||||
*/
|
||||
class AudioLevelIndicator extends Component<Props> {
|
||||
/**
|
||||
|
||||
@@ -29,7 +29,7 @@ import './styles';
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* {@link JitsiConference} that needs authentication - will hold a valid
|
||||
* {@link JitsiConference} That needs authentication - will hold a valid
|
||||
* value in XMPP login + guest access mode.
|
||||
*/
|
||||
_conference: Object,
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* {@link JitsiConference} that needs authentication - will hold a valid
|
||||
* {@link JitsiConference} That needs authentication - will hold a valid
|
||||
* value in XMPP login + guest access mode.
|
||||
*/
|
||||
_conference: Object,
|
||||
|
||||
@@ -31,11 +31,13 @@ import { isEnabledFromState } from './functions';
|
||||
export const approveParticipantAudio = (id: string) => (dispatch: Function, getState: Function) => {
|
||||
const state = getState();
|
||||
const { conference } = getConferenceState(state);
|
||||
const participant = getParticipantById(state, id);
|
||||
|
||||
const isAudioModerationOn = isEnabledFromState(MEDIA_TYPE.AUDIO, state);
|
||||
const isVideoModerationOn = isEnabledFromState(MEDIA_TYPE.VIDEO, state);
|
||||
const isVideoForceMuted = isForceMuted(participant, MEDIA_TYPE.VIDEO, state);
|
||||
|
||||
if (isAudioModerationOn || !isVideoModerationOn) {
|
||||
if (isAudioModerationOn || !isVideoModerationOn || !isVideoForceMuted) {
|
||||
conference.avModerationApprove(MEDIA_TYPE.AUDIO, id);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -8,6 +8,7 @@ import { MEDIA_TYPE } from '../base/media';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
getRemoteParticipants,
|
||||
hasRaisedHand,
|
||||
isLocalParticipantModerator,
|
||||
isParticipantModerator,
|
||||
PARTICIPANT_UPDATED,
|
||||
@@ -134,7 +135,7 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
||||
if (isLocalParticipantModerator(state)) {
|
||||
|
||||
// this is handled only by moderators
|
||||
if (participant.raisedHand) {
|
||||
if (hasRaisedHand(participant)) {
|
||||
// if participant raises hand show notification
|
||||
!isParticipantApproved(participant.id, MEDIA_TYPE.AUDIO)(state)
|
||||
&& dispatch(participantPendingAudio(participant));
|
||||
@@ -148,7 +149,7 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
||||
|
||||
// this is the granted moderator case
|
||||
getRemoteParticipants(state).forEach(p => {
|
||||
p.raisedHand && !isParticipantApproved(p.id, MEDIA_TYPE.AUDIO)(state)
|
||||
hasRaisedHand(p) && !isParticipantApproved(p.id, MEDIA_TYPE.AUDIO)(state)
|
||||
&& dispatch(participantPendingAudio(p));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -259,5 +259,5 @@ export default class BaseApp extends Component<*, State> {
|
||||
*
|
||||
* @returns {React$Element}
|
||||
*/
|
||||
_renderDialogContainer: () => React$Element<*>
|
||||
_renderDialogContainer: () => React$Element<*>;
|
||||
}
|
||||
|
||||
@@ -12,13 +12,6 @@ ReducerRegistry.register('features/base/app', (state = {}, action) => {
|
||||
if (state.app !== app) {
|
||||
return {
|
||||
...state,
|
||||
|
||||
/**
|
||||
* The one and only (i.e. singleton) {@link BaseApp} instance
|
||||
* which is currently mounted.
|
||||
*
|
||||
* @type {BaseApp}
|
||||
*/
|
||||
app
|
||||
};
|
||||
}
|
||||
|
||||
@@ -38,12 +38,12 @@ export type Props = {
|
||||
|
||||
/**
|
||||
* Display name of the entity to render an avatar for (if any). This is handy when we need
|
||||
* an avatar for a non-participasnt entity (e.g. a recent list item).
|
||||
* an avatar for a non-participasnt entity (e.g. A recent list item).
|
||||
*/
|
||||
displayName?: string,
|
||||
|
||||
/**
|
||||
* Whether or not to update the background color of the avatar
|
||||
* Whether or not to update the background color of the avatar.
|
||||
*/
|
||||
dynamicColor?: Boolean,
|
||||
|
||||
@@ -220,7 +220,7 @@ export function _mapStateToProps(state: Object, ownProps: Props) {
|
||||
_customAvatarBackgrounds: state['features/dynamic-branding'].avatarBackgrounds,
|
||||
_initialsBase,
|
||||
_loadableAvatarUrl: _participant?.loadableAvatarUrl,
|
||||
colorBase: !colorBase && _participant ? _participant.id : colorBase
|
||||
colorBase
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ export default class StatelessAvatar extends AbstractStatelessAvatar<Props> {
|
||||
);
|
||||
}
|
||||
|
||||
_isIcon: (?string | ?Object) => boolean
|
||||
_isIcon: (?string | ?Object) => boolean;
|
||||
|
||||
/**
|
||||
* Renders a badge representing the avatar status.
|
||||
|
||||
@@ -13,7 +13,7 @@ type Props = AbstractProps & {
|
||||
className?: string,
|
||||
|
||||
/**
|
||||
* The default avatar URL if we want to override the app bundled one (e.g. AlwaysOnTop)
|
||||
* The default avatar URL if we want to override the app bundled one (e.g. AlwaysOnTop).
|
||||
*/
|
||||
defaultAvatar?: string,
|
||||
|
||||
@@ -157,5 +157,5 @@ export default class StatelessAvatar extends AbstractStatelessAvatar<Props> {
|
||||
return '';
|
||||
}
|
||||
|
||||
_isIcon: (?string | ?Object) => boolean
|
||||
_isIcon: (?string | ?Object) => boolean;
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ export function getInitials(s: ?string) {
|
||||
let initials = '';
|
||||
|
||||
for (const w of words) {
|
||||
(initials.length < 2) && (initials += w.substr(0, 1).toUpperCase());
|
||||
(initials.length < 2) && (initials += String.fromCodePoint(w.codePointAt(0)).toUpperCase());
|
||||
}
|
||||
|
||||
return initials;
|
||||
|
||||
@@ -1,40 +1,95 @@
|
||||
// @flow
|
||||
|
||||
import React, { useState } from 'react';
|
||||
/* eslint-disable react/jsx-no-bind */
|
||||
|
||||
import { withStyles } from '@material-ui/styles';
|
||||
import clsx from 'clsx';
|
||||
import React, { useEffect, useState } from 'react';
|
||||
|
||||
import { Icon, IconCheck, IconCopy } from '../../base/icons';
|
||||
import { withPixelLineHeight } from '../styles/functions.web';
|
||||
import { copyText } from '../util';
|
||||
|
||||
|
||||
const styles = theme => {
|
||||
return {
|
||||
copyButton: {
|
||||
...withPixelLineHeight(theme.typography.bodyLongRegular),
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
padding: '8px 8px 8px 16px',
|
||||
marginTop: 5,
|
||||
width: 'calc(100% - 24px)',
|
||||
height: 24,
|
||||
|
||||
background: theme.palette.action01,
|
||||
cursor: 'pointer',
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.action01Hover,
|
||||
fontWeight: 600
|
||||
},
|
||||
|
||||
'&.clicked': {
|
||||
background: theme.palette.success02
|
||||
},
|
||||
|
||||
'& > div > svg > path': {
|
||||
fill: theme.palette.text01
|
||||
}
|
||||
},
|
||||
content: {
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
maxWidth: 292,
|
||||
marginRight: 16,
|
||||
|
||||
'&.selected': {
|
||||
fontWeight: 600
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
let mounted;
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Css class to apply on container
|
||||
* An object containing the CSS classes.
|
||||
*/
|
||||
classes: Object,
|
||||
|
||||
/**
|
||||
* Css class to apply on container.
|
||||
*/
|
||||
className: string,
|
||||
|
||||
/**
|
||||
* The displayed text
|
||||
* The displayed text.
|
||||
*/
|
||||
displayedText: string,
|
||||
|
||||
/**
|
||||
* The text that needs to be copied (might differ from the displayedText)
|
||||
* The text that needs to be copied (might differ from the displayedText).
|
||||
*/
|
||||
textToCopy: string,
|
||||
|
||||
/**
|
||||
* The text displayed on mouse hover
|
||||
* The text displayed on mouse hover.
|
||||
*/
|
||||
textOnHover: string,
|
||||
|
||||
/**
|
||||
* The text displayed on copy success
|
||||
* The text displayed on copy success.
|
||||
*/
|
||||
textOnCopySuccess: string,
|
||||
|
||||
/**
|
||||
* The id of the button
|
||||
* The id of the button.
|
||||
*/
|
||||
id?: string,
|
||||
};
|
||||
@@ -44,10 +99,18 @@ type Props = {
|
||||
*
|
||||
* @returns {React$Element<any>}
|
||||
*/
|
||||
function CopyButton({ className, displayedText, textToCopy, textOnHover, textOnCopySuccess, id }: Props) {
|
||||
function CopyButton({ classes, className, displayedText, textToCopy, textOnHover, textOnCopySuccess, id }: Props) {
|
||||
const [ isClicked, setIsClicked ] = useState(false);
|
||||
const [ isHovered, setIsHovered ] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
mounted = true;
|
||||
|
||||
return () => {
|
||||
mounted = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Click handler for the element.
|
||||
*
|
||||
@@ -62,7 +125,10 @@ function CopyButton({ className, displayedText, textToCopy, textOnHover, textOnC
|
||||
setIsClicked(true);
|
||||
|
||||
setTimeout(() => {
|
||||
setIsClicked(false);
|
||||
// avoid: Can't perform a React state update on an unmounted component
|
||||
if (mounted) {
|
||||
setIsClicked(false);
|
||||
}
|
||||
}, 2500);
|
||||
}
|
||||
}
|
||||
@@ -110,7 +176,7 @@ function CopyButton({ className, displayedText, textToCopy, textOnHover, textOnC
|
||||
if (isClicked) {
|
||||
return (
|
||||
<>
|
||||
<div className = 'copy-button-content selected'>
|
||||
<div className = { clsx(classes.content, 'selected') }>
|
||||
<span role = { 'alert' }>{ textOnCopySuccess }</span>
|
||||
</div>
|
||||
<Icon src = { IconCheck } />
|
||||
@@ -120,8 +186,8 @@ function CopyButton({ className, displayedText, textToCopy, textOnHover, textOnC
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className = 'copy-button-content'>
|
||||
{isHovered ? textOnHover : displayedText}
|
||||
<div className = { `${classes.copyButton}-content` }>
|
||||
{ isHovered ? textOnHover : displayedText }
|
||||
</div>
|
||||
<Icon src = { IconCopy } />
|
||||
</>
|
||||
@@ -131,7 +197,7 @@ function CopyButton({ className, displayedText, textToCopy, textOnHover, textOnC
|
||||
return (
|
||||
<div
|
||||
aria-label = { textOnHover }
|
||||
className = { `${className} copy-button${isClicked ? ' clicked' : ''}` }
|
||||
className = { clsx(className, classes.copyButton, isClicked ? ' clicked' : '') }
|
||||
id = { id }
|
||||
onBlur = { onHoverOut }
|
||||
onClick = { onClick }
|
||||
@@ -150,4 +216,4 @@ CopyButton.defaultProps = {
|
||||
className: ''
|
||||
};
|
||||
|
||||
export default CopyButton;
|
||||
export default withStyles(styles)(CopyButton);
|
||||
|
||||
67
react/features/base/components/buttons/QuickActionButton.js
Normal file
67
react/features/base/components/buttons/QuickActionButton.js
Normal file
@@ -0,0 +1,67 @@
|
||||
// @flow
|
||||
|
||||
import { makeStyles } from '@material-ui/styles';
|
||||
import React from 'react';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Label used for accessibility.
|
||||
*/
|
||||
accessibilityLabel: string,
|
||||
|
||||
/**
|
||||
* Additional class name for custom styles.
|
||||
*/
|
||||
className: string,
|
||||
|
||||
/**
|
||||
* Children of the component.
|
||||
*/
|
||||
children: string | React$Node,
|
||||
|
||||
/**
|
||||
* Click handler.
|
||||
*/
|
||||
onClick: Function,
|
||||
|
||||
/**
|
||||
* Data test id.
|
||||
*/
|
||||
testId?: string
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(theme => {
|
||||
return {
|
||||
button: {
|
||||
backgroundColor: theme.palette.action01,
|
||||
color: theme.palette.text01,
|
||||
borderRadius: `${theme.shape.borderRadius}px`,
|
||||
...theme.typography.labelBold,
|
||||
lineHeight: `${theme.typography.labelBold.lineHeight}px`,
|
||||
padding: '8px 12px',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
border: 0,
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.action01Hover
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const QuickActionButton = ({ accessibilityLabel, className, children, onClick, testId }: Props) => {
|
||||
const styles = useStyles();
|
||||
|
||||
return (<button
|
||||
aria-label = { accessibilityLabel }
|
||||
className = { `${styles.button} ${className}` }
|
||||
data-testid = { testId }
|
||||
onClick = { onClick }>
|
||||
{children}
|
||||
</button>);
|
||||
};
|
||||
|
||||
export default QuickActionButton;
|
||||
177
react/features/base/components/context-menu/ContextMenu.js
Normal file
177
react/features/base/components/context-menu/ContextMenu.js
Normal file
@@ -0,0 +1,177 @@
|
||||
// @flow
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import clsx from 'clsx';
|
||||
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { getComputedOuterHeight } from '../../../participants-pane/functions';
|
||||
import { Drawer, JitsiPortal } from '../../../toolbox/components/web';
|
||||
import { showOverflowDrawer } from '../../../toolbox/functions.web';
|
||||
import participantsPaneTheme from '../themes/participantsPaneTheme.json';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Children of the context menu.
|
||||
*/
|
||||
children: React$Node,
|
||||
|
||||
/**
|
||||
* Class name for context menu. Used to overwrite default styles.
|
||||
*/
|
||||
className?: string,
|
||||
|
||||
/**
|
||||
* The entity for which the context menu is displayed.
|
||||
*/
|
||||
entity?: Object,
|
||||
|
||||
/**
|
||||
* Whether or not the menu is hidden. Used to overwrite the internal isHidden.
|
||||
*/
|
||||
hidden?: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not drawer should be open.
|
||||
*/
|
||||
isDrawerOpen: boolean,
|
||||
|
||||
/**
|
||||
* Target elements against which positioning calculations are made.
|
||||
*/
|
||||
offsetTarget?: HTMLElement,
|
||||
|
||||
/**
|
||||
* Callback for click on an item in the menu.
|
||||
*/
|
||||
onClick?: Function,
|
||||
|
||||
/**
|
||||
* Callback for drawer close.
|
||||
*/
|
||||
onDrawerClose: Function,
|
||||
|
||||
/**
|
||||
* Callback for the mouse entering the component.
|
||||
*/
|
||||
onMouseEnter?: Function,
|
||||
|
||||
/**
|
||||
* Callback for the mouse leaving the component.
|
||||
*/
|
||||
onMouseLeave: Function
|
||||
};
|
||||
|
||||
const useStyles = makeStyles(theme => {
|
||||
return {
|
||||
contextMenu: {
|
||||
backgroundColor: theme.palette.ui02,
|
||||
borderRadius: `${theme.shape.borderRadius / 2}px`,
|
||||
boxShadow: '0px 3px 16px rgba(0, 0, 0, 0.6), 0px 0px 4px 1px rgba(0, 0, 0, 0.25)',
|
||||
color: theme.palette.text01,
|
||||
...theme.typography.bodyShortRegular,
|
||||
lineHeight: `${theme.typography.bodyShortRegular.lineHeight}px`,
|
||||
marginTop: `${(participantsPaneTheme.panePadding * 2) + theme.typography.bodyShortRegular.fontSize}px`,
|
||||
position: 'absolute',
|
||||
right: `${participantsPaneTheme.panePadding}px`,
|
||||
top: 0,
|
||||
zIndex: 2
|
||||
},
|
||||
|
||||
contextMenuHidden: {
|
||||
pointerEvents: 'none',
|
||||
visibility: 'hidden'
|
||||
},
|
||||
|
||||
drawer: {
|
||||
|
||||
'& > div': {
|
||||
...theme.typography.bodyShortRegularLarge,
|
||||
lineHeight: `${theme.typography.bodyShortRegularLarge.lineHeight}px`,
|
||||
|
||||
'& svg': {
|
||||
fill: theme.palette.icon01
|
||||
}
|
||||
},
|
||||
|
||||
'& > *:first-child': {
|
||||
paddingTop: '15px!important'
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const ContextMenu = ({
|
||||
children,
|
||||
className,
|
||||
entity,
|
||||
hidden,
|
||||
isDrawerOpen,
|
||||
offsetTarget,
|
||||
onClick,
|
||||
onDrawerClose,
|
||||
onMouseEnter,
|
||||
onMouseLeave
|
||||
}: Props) => {
|
||||
const [ isHidden, setIsHidden ] = useState(true);
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const styles = useStyles();
|
||||
const _overflowDrawer = useSelector(showOverflowDrawer);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (_overflowDrawer) {
|
||||
return;
|
||||
}
|
||||
if (entity && offsetTarget
|
||||
&& containerRef.current
|
||||
&& offsetTarget?.offsetParent
|
||||
&& offsetTarget.offsetParent instanceof HTMLElement
|
||||
) {
|
||||
const { current: container } = containerRef;
|
||||
const { offsetTop, offsetParent: { offsetHeight, scrollTop } } = offsetTarget;
|
||||
const outerHeight = getComputedOuterHeight(container);
|
||||
|
||||
container.style.top = offsetTop + outerHeight > offsetHeight + scrollTop
|
||||
? `${offsetTop - outerHeight}`
|
||||
: `${offsetTop}`;
|
||||
|
||||
setIsHidden(false);
|
||||
} else {
|
||||
setIsHidden(true);
|
||||
}
|
||||
}, [ entity, offsetTarget, _overflowDrawer ]);
|
||||
|
||||
useEffect(() => {
|
||||
if (hidden !== undefined) {
|
||||
setIsHidden(hidden);
|
||||
}
|
||||
}, [ hidden ]);
|
||||
|
||||
return _overflowDrawer
|
||||
? <JitsiPortal>
|
||||
<Drawer
|
||||
isOpen = { isDrawerOpen && _overflowDrawer }
|
||||
onClose = { onDrawerClose }>
|
||||
<div
|
||||
className = { styles.drawer }
|
||||
onClick = { onDrawerClose }>
|
||||
{children}
|
||||
</div>
|
||||
</Drawer>
|
||||
</JitsiPortal>
|
||||
: <div
|
||||
className = { clsx(participantsPaneTheme.ignoredChildClassName,
|
||||
styles.contextMenu,
|
||||
isHidden && styles.contextMenuHidden,
|
||||
className
|
||||
) }
|
||||
onClick = { onClick }
|
||||
onMouseEnter = { onMouseEnter }
|
||||
onMouseLeave = { onMouseLeave }
|
||||
ref = { containerRef }>
|
||||
{children}
|
||||
</div>
|
||||
;
|
||||
};
|
||||
|
||||
export default ContextMenu;
|
||||
@@ -0,0 +1,136 @@
|
||||
// @flow
|
||||
import { makeStyles } from '@material-ui/core';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { showOverflowDrawer } from '../../../toolbox/functions.web';
|
||||
import { Icon } from '../../icons';
|
||||
|
||||
export type Action = {
|
||||
|
||||
/**
|
||||
* Label used for accessibility.
|
||||
*/
|
||||
accessibilityLabel: string,
|
||||
|
||||
/**
|
||||
* CSS class name used for custom styles.
|
||||
*/
|
||||
className?: string,
|
||||
|
||||
/**
|
||||
* Custom icon. If used, the icon prop is ignored.
|
||||
* Used to allow custom children instead of just the default icons.
|
||||
*/
|
||||
customIcon?: React$Node,
|
||||
|
||||
/**
|
||||
* Id of the action container.
|
||||
*/
|
||||
id?: string,
|
||||
|
||||
/**
|
||||
* Default icon for action.
|
||||
*/
|
||||
icon?: Function,
|
||||
|
||||
/**
|
||||
* Click handler.
|
||||
*/
|
||||
onClick?: Function,
|
||||
|
||||
/**
|
||||
* Action text.
|
||||
*/
|
||||
text: string
|
||||
}
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* List of actions in this group.
|
||||
*/
|
||||
actions?: Array<Action>,
|
||||
|
||||
/**
|
||||
* The children of the component.
|
||||
*/
|
||||
children?: React$Node,
|
||||
};
|
||||
|
||||
|
||||
const useStyles = makeStyles(theme => {
|
||||
return {
|
||||
contextMenuItemGroup: {
|
||||
'&:not(:empty)': {
|
||||
padding: `${theme.spacing(2)}px 0`
|
||||
},
|
||||
|
||||
'& + &:not(:empty)': {
|
||||
borderTop: `1px solid ${theme.palette.ui04}`
|
||||
}
|
||||
},
|
||||
|
||||
contextMenuItem: {
|
||||
alignItems: 'center',
|
||||
cursor: 'pointer',
|
||||
display: 'flex',
|
||||
minHeight: '40px',
|
||||
padding: '10px 16px',
|
||||
boxSizing: 'border-box',
|
||||
|
||||
'& > *:not(:last-child)': {
|
||||
marginRight: `${theme.spacing(3)}px`
|
||||
},
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.ui04
|
||||
}
|
||||
},
|
||||
|
||||
contextMenuItemDrawer: {
|
||||
padding: '12px 16px'
|
||||
},
|
||||
|
||||
contextMenuItemIcon: {
|
||||
'& svg': {
|
||||
fill: theme.palette.icon01
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const ContextMenuItemGroup = ({
|
||||
actions,
|
||||
children
|
||||
}: Props) => {
|
||||
const styles = useStyles();
|
||||
const _overflowDrawer = useSelector(showOverflowDrawer);
|
||||
|
||||
return (
|
||||
<div className = { styles.contextMenuItemGroup }>
|
||||
{children}
|
||||
{actions && actions.map(({ accessibilityLabel, className, customIcon, id, icon, onClick, text }) => (
|
||||
<div
|
||||
aria-label = { accessibilityLabel }
|
||||
className = { clsx(styles.contextMenuItem,
|
||||
_overflowDrawer && styles.contextMenuItemDrawer,
|
||||
className
|
||||
) }
|
||||
id = { id }
|
||||
key = { text }
|
||||
onClick = { onClick }>
|
||||
{customIcon ? customIcon
|
||||
: icon && <Icon
|
||||
className = { styles.contextMenuItemIcon }
|
||||
size = { 20 }
|
||||
src = { icon } />}
|
||||
<span>{text}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ContextMenuItemGroup;
|
||||
@@ -0,0 +1,79 @@
|
||||
// @flow
|
||||
|
||||
import { useCallback, useRef, useState } from 'react';
|
||||
|
||||
import { findAncestorByClass } from '../../../participants-pane/functions';
|
||||
|
||||
type NullProto = {
|
||||
[key: string]: any,
|
||||
__proto__: null
|
||||
};
|
||||
|
||||
type RaiseContext = NullProto | {|
|
||||
|
||||
/**
|
||||
* Target elements against which positioning calculations are made.
|
||||
*/
|
||||
offsetTarget?: HTMLElement,
|
||||
|
||||
/**
|
||||
* The entity for which the menu is context menu is raised.
|
||||
*/
|
||||
entity?: string | Object,
|
||||
|};
|
||||
|
||||
const initialState = Object.freeze(Object.create(null));
|
||||
|
||||
const useContextMenu = () => {
|
||||
const [ raiseContext, setRaiseContext ] = useState < RaiseContext >(initialState);
|
||||
const isMouseOverMenu = useRef(false);
|
||||
|
||||
const lowerMenu = useCallback((force: boolean | Object = false) => {
|
||||
/**
|
||||
* We are tracking mouse movement over the active participant item and
|
||||
* the context menu. Due to the order of enter/leave events, we need to
|
||||
* defer checking if the mouse is over the context menu with
|
||||
* queueMicrotask.
|
||||
*/
|
||||
window.queueMicrotask(() => {
|
||||
if (isMouseOverMenu.current && !(force === true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (raiseContext !== initialState) {
|
||||
setRaiseContext(initialState);
|
||||
}
|
||||
});
|
||||
}, [ raiseContext ]);
|
||||
|
||||
const raiseMenu = useCallback((entity: string | Object, target: EventTarget) => {
|
||||
setRaiseContext({
|
||||
entity,
|
||||
offsetTarget: findAncestorByClass(target, 'list-item-container')
|
||||
});
|
||||
}, [ raiseContext ]);
|
||||
|
||||
const toggleMenu = useCallback((entity: string | Object) => (e: MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
const { entity: raisedEntity } = raiseContext;
|
||||
|
||||
if (raisedEntity && raisedEntity === entity) {
|
||||
lowerMenu();
|
||||
} else {
|
||||
raiseMenu(entity, e.target);
|
||||
}
|
||||
}, [ raiseContext ]);
|
||||
|
||||
const menuEnter = useCallback(() => {
|
||||
isMouseOverMenu.current = true;
|
||||
}, []);
|
||||
|
||||
const menuLeave = useCallback(() => {
|
||||
isMouseOverMenu.current = false;
|
||||
lowerMenu();
|
||||
}, [ lowerMenu ]);
|
||||
|
||||
return [ lowerMenu, raiseMenu, toggleMenu, menuEnter, menuLeave, raiseContext ];
|
||||
};
|
||||
|
||||
export default useContextMenu;
|
||||
4
react/features/base/components/index.js
Normal file
4
react/features/base/components/index.js
Normal file
@@ -0,0 +1,4 @@
|
||||
export { default as ContextMenu } from './context-menu/ContextMenu';
|
||||
export { default as ContextMenuItemGroup } from './context-menu/ContextMenuItemGroup';
|
||||
export { default as ListItem } from './participants-pane-list/ListItem';
|
||||
export { default as QuickActionButton } from './buttons/QuickActionButton';
|
||||
@@ -0,0 +1,280 @@
|
||||
// @flow
|
||||
|
||||
import { makeStyles } from '@material-ui/styles';
|
||||
import clsx from 'clsx';
|
||||
import React from 'react';
|
||||
|
||||
import { ACTION_TRIGGER } from '../../../participants-pane/constants';
|
||||
import { isMobileBrowser } from '../../environment/utils';
|
||||
import participantsPaneTheme from '../themes/participantsPaneTheme.json';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* List item actions.
|
||||
*/
|
||||
actions: React$Node,
|
||||
|
||||
/**
|
||||
* List item container class name.
|
||||
*/
|
||||
className: string,
|
||||
|
||||
/**
|
||||
* Icon to be displayed on the list item. (Avatar for participants).
|
||||
*/
|
||||
icon: React$Node,
|
||||
|
||||
/**
|
||||
* Id of the container.
|
||||
*/
|
||||
id: string,
|
||||
|
||||
/**
|
||||
* Whether or not the actions should be hidden.
|
||||
*/
|
||||
hideActions?: Boolean,
|
||||
|
||||
/**
|
||||
* Indicators to be displayed on the list item.
|
||||
*/
|
||||
indicators?: React$Node,
|
||||
|
||||
/**
|
||||
* Whether or not the item is highlighted.
|
||||
*/
|
||||
isHighlighted?: boolean,
|
||||
|
||||
/**
|
||||
* Click handler.
|
||||
*/
|
||||
onClick: Function,
|
||||
|
||||
/**
|
||||
* Long press handler.
|
||||
*/
|
||||
onLongPress: Function,
|
||||
|
||||
/**
|
||||
* Mouse leave handler.
|
||||
*/
|
||||
onMouseLeave: Function,
|
||||
|
||||
/**
|
||||
* Data test id.
|
||||
*/
|
||||
testId?: string,
|
||||
|
||||
/**
|
||||
* Text children to be displayed on the list item.
|
||||
*/
|
||||
textChildren: React$Node | string,
|
||||
|
||||
/**
|
||||
* The actions trigger. Can be Hover or Permanent.
|
||||
*/
|
||||
trigger: string
|
||||
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(theme => {
|
||||
return {
|
||||
container: {
|
||||
alignItems: 'center',
|
||||
color: theme.palette.text01,
|
||||
display: 'flex',
|
||||
...theme.typography.bodyShortRegular,
|
||||
lineHeight: `${theme.typography.bodyShortRegular.lineHeight}px`,
|
||||
margin: `0 -${participantsPaneTheme.panePadding}px`,
|
||||
padding: `0 ${participantsPaneTheme.panePadding}px`,
|
||||
position: 'relative',
|
||||
boxShadow: 'inset 0px -1px 0px rgba(255, 255, 255, 0.15)',
|
||||
minHeight: '40px',
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.action02Active,
|
||||
|
||||
'& .indicators': {
|
||||
display: 'none'
|
||||
},
|
||||
|
||||
'& .actions': {
|
||||
display: 'flex',
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.action02Active}`,
|
||||
backgroundColor: theme.palette.action02Active
|
||||
}
|
||||
},
|
||||
|
||||
[`@media(max-width: ${participantsPaneTheme.MD_BREAKPOINT})`]: {
|
||||
...theme.typography.bodyShortRegularLarge,
|
||||
lineHeight: `${theme.typography.bodyShortRegularLarge.lineHeight}px`,
|
||||
padding: `${theme.spacing(2)}px ${participantsPaneTheme.panePadding}px`
|
||||
}
|
||||
},
|
||||
|
||||
highlighted: {
|
||||
backgroundColor: theme.palette.action02Active
|
||||
},
|
||||
|
||||
detailsContainer: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
height: '100%',
|
||||
overflow: 'hidden',
|
||||
position: 'relative'
|
||||
},
|
||||
|
||||
name: {
|
||||
display: 'flex',
|
||||
flex: 1,
|
||||
marginRight: `${theme.spacing(2)}px`,
|
||||
overflow: 'hidden',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-start'
|
||||
},
|
||||
|
||||
indicators: {
|
||||
display: 'flex',
|
||||
justifyContent: 'flex-end',
|
||||
|
||||
'& > *': {
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'center'
|
||||
},
|
||||
|
||||
'& > *:not(:last-child)': {
|
||||
marginRight: `${theme.spacing(2)}px`
|
||||
},
|
||||
|
||||
'& .jitsi-icon': {
|
||||
padding: '3px'
|
||||
}
|
||||
},
|
||||
|
||||
indicatorsHidden: {
|
||||
display: 'none'
|
||||
},
|
||||
|
||||
actionsContainer: {
|
||||
display: 'none',
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.action02Active}`,
|
||||
backgroundColor: theme.palette.action02Active
|
||||
},
|
||||
|
||||
actionsPermanent: {
|
||||
display: 'flex',
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.ui01}`,
|
||||
backgroundColor: theme.palette.ui01
|
||||
},
|
||||
|
||||
actionsVisible: {
|
||||
display: 'flex',
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.action02Active}`,
|
||||
backgroundColor: theme.palette.action02Active
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
const ListItem = ({
|
||||
actions,
|
||||
className,
|
||||
icon,
|
||||
id,
|
||||
hideActions = false,
|
||||
indicators,
|
||||
isHighlighted,
|
||||
onClick,
|
||||
onLongPress,
|
||||
onMouseLeave,
|
||||
testId,
|
||||
textChildren,
|
||||
trigger
|
||||
}: Props) => {
|
||||
const styles = useStyles();
|
||||
const _isMobile = isMobileBrowser();
|
||||
let timeoutHandler;
|
||||
|
||||
/**
|
||||
* Set calling long press handler after x milliseconds.
|
||||
*
|
||||
* @param {TouchEvent} e - Touch start event.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _onTouchStart(e) {
|
||||
const target = e.touches[0].target;
|
||||
|
||||
timeoutHandler = setTimeout(() => onLongPress(target), 600);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel calling on long press after x milliseconds if the number of milliseconds is not reached
|
||||
* before a touch move(drag), or just clears the timeout.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function _onTouchMove() {
|
||||
clearTimeout(timeoutHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel calling on long press after x milliseconds if the number of milliseconds is not reached yet,
|
||||
* or just clears the timeout.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
function _onTouchEnd() {
|
||||
clearTimeout(timeoutHandler);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className = { clsx('list-item-container',
|
||||
styles.container,
|
||||
isHighlighted && styles.highlighted,
|
||||
className
|
||||
) }
|
||||
data-testid = { testId }
|
||||
id = { id }
|
||||
onClick = { onClick }
|
||||
{ ...(_isMobile
|
||||
? {
|
||||
onTouchEnd: _onTouchEnd,
|
||||
onTouchMove: _onTouchMove,
|
||||
onTouchStart: _onTouchStart
|
||||
}
|
||||
: {
|
||||
onMouseLeave
|
||||
}
|
||||
) }>
|
||||
<div> {icon} </div>
|
||||
<div className = { styles.detailsContainer }>
|
||||
<div className = { styles.name }>
|
||||
{textChildren}
|
||||
</div>
|
||||
{indicators && (
|
||||
<div
|
||||
className = { clsx('indicators',
|
||||
styles.indicators,
|
||||
(isHighlighted || trigger === ACTION_TRIGGER.PERMANENT) && styles.indicatorsHidden
|
||||
) }>
|
||||
{indicators}
|
||||
</div>
|
||||
)}
|
||||
{!hideActions && (
|
||||
<div
|
||||
className = { clsx('actions',
|
||||
styles.actionsContainer,
|
||||
trigger === ACTION_TRIGGER.PERMANENT && styles.actionsPermanent,
|
||||
isHighlighted && styles.actionsVisible
|
||||
) }>
|
||||
{actions}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListItem;
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"colors": {
|
||||
"moderationDisabled": "#E54B4B"
|
||||
},
|
||||
"headerSize": 60,
|
||||
"ignoredChildClassName": "ignore-child",
|
||||
"panePadding": 16,
|
||||
"participantsPaneWidth": 315,
|
||||
"MD_BREAKPOINT": "580px"
|
||||
}
|
||||
@@ -127,6 +127,17 @@ export const KICKED_OUT = 'KICKED_OUT';
|
||||
*/
|
||||
export const LOCK_STATE_CHANGED = 'LOCK_STATE_CHANGED';
|
||||
|
||||
/**
|
||||
* The type of (redux) action which signals that a system (non-participant) message has been received.
|
||||
*
|
||||
* {
|
||||
* type: NON_PARTICIPANT_MESSAGE_RECEIVED,
|
||||
* id: String,
|
||||
* json: Object
|
||||
* }
|
||||
*/
|
||||
export const NON_PARTICIPANT_MESSAGE_RECEIVED = 'NON_PARTICIPANT_MESSAGE_RECEIVED';
|
||||
|
||||
/**
|
||||
* The type of (redux) action which sets the peer2peer flag for the current
|
||||
* conference.
|
||||
|
||||
@@ -37,6 +37,7 @@ import {
|
||||
DATA_CHANNEL_OPENED,
|
||||
KICKED_OUT,
|
||||
LOCK_STATE_CHANGED,
|
||||
NON_PARTICIPANT_MESSAGE_RECEIVED,
|
||||
P2P_STATUS_CHANGED,
|
||||
SEND_TONES,
|
||||
SET_FOLLOW_ME,
|
||||
@@ -179,6 +180,10 @@ function _addConferenceListeners(conference, dispatch, state) {
|
||||
JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
||||
(...args) => dispatch(endpointMessageReceived(...args)));
|
||||
|
||||
conference.on(
|
||||
JitsiConferenceEvents.NON_PARTICIPANT_MESSAGE_RECEIVED,
|
||||
(...args) => dispatch(nonParticipantMessageReceived(...args)));
|
||||
|
||||
conference.on(
|
||||
JitsiConferenceEvents.PARTICIPANT_CONN_STATUS_CHANGED,
|
||||
(...args) => dispatch(participantConnectionStatusChanged(...args)));
|
||||
@@ -415,9 +420,11 @@ export function conferenceWillLeave(conference: Object) {
|
||||
/**
|
||||
* Initializes a new conference.
|
||||
*
|
||||
* @param {string} overrideRoom - Override the room to join, instead of taking it
|
||||
* from Redux.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function createConference() {
|
||||
export function createConference(overrideRoom?: string) {
|
||||
return (dispatch: Function, getState: Function) => {
|
||||
const state = getState();
|
||||
const { connection, locationURL } = state['features/base/connection'];
|
||||
@@ -432,7 +439,20 @@ export function createConference() {
|
||||
throw new Error('Cannot join a conference without a room name!');
|
||||
}
|
||||
|
||||
const conference = connection.initJitsiConference(getBackendSafeRoomName(room), getConferenceOptions(state));
|
||||
// XXX: revisit this.
|
||||
// Hide the custom domain in the room name.
|
||||
const tmp = overrideRoom || room;
|
||||
let _room = getBackendSafeRoomName(tmp);
|
||||
|
||||
if (tmp.domain) {
|
||||
// eslint-disable-next-line no-new-wrappers
|
||||
_room = new String(tmp);
|
||||
|
||||
// $FlowExpectedError
|
||||
_room.domain = tmp.domain;
|
||||
}
|
||||
|
||||
const conference = connection.initJitsiConference(_room, getConferenceOptions(state));
|
||||
|
||||
connection[JITSI_CONNECTION_CONFERENCE_KEY] = conference;
|
||||
|
||||
@@ -525,6 +545,25 @@ export function lockStateChanged(conference: Object, locked: boolean) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals that a non participant endpoint message has been received.
|
||||
*
|
||||
* @param {string} id - The resource id of the sender.
|
||||
* @param {Object} json - The json carried by the endpoint message.
|
||||
* @returns {{
|
||||
* type: NON_PARTICIPANT_MESSAGE_RECEIVED,
|
||||
* id: Object,
|
||||
* json: Object
|
||||
* }}
|
||||
*/
|
||||
export function nonParticipantMessageReceived(id: String, json: Object) {
|
||||
return {
|
||||
type: NON_PARTICIPANT_MESSAGE_RECEIVED,
|
||||
id,
|
||||
json
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the known state of start muted policies.
|
||||
*
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user