mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-09-12 07:38:42 +00:00
Compare commits
2 Commits
android-sd
...
release-48
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a1d708d37 | ||
|
|
b1092b28d3 |
@@ -25,5 +25,5 @@ android.enableDexingArtifactTransform.desugaring=false
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
|
||||
appVersion=21.1.0
|
||||
sdkVersion=3.4.0
|
||||
appVersion=21.0.0
|
||||
sdkVersion=3.2.0
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
/* global APP, JitsiMeetJS, config, interfaceConfig */
|
||||
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
import EventEmitter from 'events';
|
||||
import Logger from 'jitsi-meet-logger';
|
||||
|
||||
import { openConnection } from './connection';
|
||||
import { ENDPOINT_TEXT_MESSAGE_NAME } from './modules/API/constants';
|
||||
import { AUDIO_ONLY_SCREEN_SHARE_NO_TRACK } from './modules/UI/UIErrors';
|
||||
import AuthHandler from './modules/UI/authentication/AuthHandler';
|
||||
import UIUtil from './modules/UI/util/UIUtil';
|
||||
import mediaDeviceHelper from './modules/devices/mediaDeviceHelper';
|
||||
@@ -127,7 +125,6 @@ import {
|
||||
makePrecallTest
|
||||
} from './react/features/prejoin';
|
||||
import { disableReceiver, stopReceiver } from './react/features/remote-control';
|
||||
import { setScreenAudioShareState, isScreenAudioShared } from './react/features/screen-share/';
|
||||
import { toggleScreenshotCaptureEffect } from './react/features/screenshot-capture';
|
||||
import { setSharedVideoStatus } from './react/features/shared-video/actions';
|
||||
import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/AudioMixerEffect';
|
||||
@@ -390,8 +387,7 @@ class ConferenceConnector {
|
||||
*
|
||||
*/
|
||||
connect() {
|
||||
// the local storage overrides here and in connection.js can be used by jibri
|
||||
room.join(jitsiLocalStorage.getItem('xmpp_conference_password_override'));
|
||||
room.join();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1548,8 +1544,6 @@ export default {
|
||||
this._desktopAudioStream = undefined;
|
||||
}
|
||||
|
||||
APP.store.dispatch(setScreenAudioShareState(false));
|
||||
|
||||
if (didHaveVideo) {
|
||||
promise = promise.then(() => createLocalTracksF({ devices: [ 'video' ] }))
|
||||
.then(([ stream ]) => {
|
||||
@@ -1666,23 +1660,6 @@ export default {
|
||||
= this._turnScreenSharingOff.bind(this, didHaveVideo);
|
||||
|
||||
const desktopVideoStream = desktopStreams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
|
||||
const dekstopAudioStream = desktopStreams.find(stream => stream.getType() === MEDIA_TYPE.AUDIO);
|
||||
|
||||
if (dekstopAudioStream) {
|
||||
dekstopAudioStream.on(
|
||||
JitsiTrackEvents.LOCAL_TRACK_STOPPED,
|
||||
() => {
|
||||
logger.debug(`Local screensharing audio track stopped. ${this.isSharingScreen}`);
|
||||
|
||||
// Handle case where screen share was stopped from the browsers 'screen share in progress'
|
||||
// window. If audio screen sharing is stopped via the normal UX flow this point shouldn't
|
||||
// be reached.
|
||||
isScreenAudioShared(APP.store.getState())
|
||||
&& this._untoggleScreenSharing
|
||||
&& this._untoggleScreenSharing();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (desktopVideoStream) {
|
||||
desktopVideoStream.on(
|
||||
@@ -1851,28 +1828,14 @@ export default {
|
||||
|
||||
return this._createDesktopTrack(options)
|
||||
.then(async streams => {
|
||||
let desktopVideoStream = streams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
|
||||
|
||||
this._desktopAudioStream = streams.find(stream => stream.getType() === MEDIA_TYPE.AUDIO);
|
||||
|
||||
const { audioOnly = false } = options;
|
||||
|
||||
// If we're in audio only mode dispose of the video track otherwise the screensharing state will be
|
||||
// inconsistent.
|
||||
if (audioOnly) {
|
||||
desktopVideoStream.dispose();
|
||||
desktopVideoStream = undefined;
|
||||
|
||||
if (!this._desktopAudioStream) {
|
||||
return Promise.reject(AUDIO_ONLY_SCREEN_SHARE_NO_TRACK);
|
||||
}
|
||||
}
|
||||
const desktopVideoStream = streams.find(stream => stream.getType() === MEDIA_TYPE.VIDEO);
|
||||
|
||||
if (desktopVideoStream) {
|
||||
logger.debug(`_switchToScreenSharing is using ${desktopVideoStream} for useVideoStream`);
|
||||
await this.useVideoStream(desktopVideoStream);
|
||||
}
|
||||
|
||||
this._desktopAudioStream = streams.find(stream => stream.getType() === MEDIA_TYPE.AUDIO);
|
||||
|
||||
if (this._desktopAudioStream) {
|
||||
// If there is a localAudio stream, mix in the desktop audio stream captured by the screen sharing
|
||||
@@ -1885,9 +1848,7 @@ export default {
|
||||
// If no local stream is present ( i.e. no input audio devices) we use the screen share audio
|
||||
// stream as we would use a regular stream.
|
||||
await this.useAudioStream(this._desktopAudioStream);
|
||||
|
||||
}
|
||||
APP.store.dispatch(setScreenAudioShareState(true));
|
||||
}
|
||||
})
|
||||
.then(() => {
|
||||
@@ -1955,9 +1916,6 @@ export default {
|
||||
} else if (error.name === JitsiTrackErrors.SCREENSHARING_GENERIC_ERROR) {
|
||||
descriptionKey = 'dialog.screenSharingFailed';
|
||||
titleKey = 'dialog.screenSharingFailedTitle';
|
||||
} else if (error === AUDIO_ONLY_SCREEN_SHARE_NO_TRACK) {
|
||||
descriptionKey = 'notify.screenShareNoAudio';
|
||||
titleKey = 'notify.screenShareNoAudioTitle';
|
||||
}
|
||||
|
||||
APP.UI.messageHandler.showError({
|
||||
@@ -2449,9 +2407,7 @@ export default {
|
||||
});
|
||||
|
||||
APP.UI.addListener(
|
||||
UIEvents.TOGGLE_SCREENSHARING, audioOnly => {
|
||||
this.toggleScreenSharing(undefined, { audioOnly });
|
||||
}
|
||||
UIEvents.TOGGLE_SCREENSHARING, this.toggleScreenSharing.bind(this)
|
||||
);
|
||||
|
||||
/* eslint-disable max-params */
|
||||
|
||||
12
config.js
12
config.js
@@ -123,7 +123,7 @@ var config = {
|
||||
// opusMaxAverageBitrate: 20000,
|
||||
|
||||
// Enables support for opus-red (redundancy for Opus).
|
||||
// enableOpusRed: false,
|
||||
// enableOpusRed: false
|
||||
|
||||
// Video
|
||||
|
||||
@@ -428,7 +428,7 @@ var config = {
|
||||
// toolbarButtons: [
|
||||
// 'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',
|
||||
// 'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
|
||||
// 'livestreaming', 'etherpad', 'sharedvideo', 'shareaudio', 'settings', 'raisehand',
|
||||
// 'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',
|
||||
// 'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
|
||||
// 'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone', 'security'
|
||||
// ],
|
||||
@@ -498,7 +498,7 @@ var config = {
|
||||
|
||||
// If set to true, it will prefer to use H.264 for P2P calls (if H.264
|
||||
// is supported). This setting is deprecated, use preferredCodec instead.
|
||||
// preferH264: true,
|
||||
// preferH264: true
|
||||
|
||||
// Provides a way to set the video codec preference on the p2p connection. Acceptable
|
||||
// codec values are 'VP8', 'VP9' and 'H264'.
|
||||
@@ -540,7 +540,7 @@ var config = {
|
||||
// The interval at which rtcstats will poll getStats, defaults to 1000ms.
|
||||
// If the value is set to 0 getStats won't be polled and the rtcstats client
|
||||
// will only send data related to RTCPeerConnection events.
|
||||
// rtcstatsPolIInterval: 1000,
|
||||
// rtcstatsPolIInterval: 1000
|
||||
|
||||
// Array of script URLs to load as lib-jitsi-meet "analytics handlers".
|
||||
// scriptURLs: [
|
||||
@@ -689,13 +689,13 @@ var config = {
|
||||
// disableTileView: true,
|
||||
|
||||
// Hides the conference subject
|
||||
// hideConferenceSubject: true,
|
||||
// hideConferenceSubject: true
|
||||
|
||||
// Hides the conference timer.
|
||||
// hideConferenceTimer: true,
|
||||
|
||||
// Hides the participants stats
|
||||
// hideParticipantsStats: true,
|
||||
// hideParticipantsStats: true
|
||||
|
||||
// Sets the conference subject
|
||||
// subject: 'Conference Subject',
|
||||
|
||||
@@ -1,37 +1,68 @@
|
||||
.label {
|
||||
align-items: center;
|
||||
background: #36383C;
|
||||
border-radius: 3px;
|
||||
color: #fff;
|
||||
.large-video-labels {
|
||||
display: flex;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
height: 28px;
|
||||
margin: 0 0 4px 4px;
|
||||
padding: 0 8px;
|
||||
position: absolute;
|
||||
top: 30px;
|
||||
right: 30px;
|
||||
transition: right 0.5s;
|
||||
z-index: $labelsZ;
|
||||
|
||||
&--green {
|
||||
background: #31B76A;
|
||||
.circular-label {
|
||||
align-items: center;
|
||||
color: white;
|
||||
display: flex;
|
||||
font-weight: bold;
|
||||
justify-content: center;
|
||||
margin-left: 8px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
&--red {
|
||||
background: #E34F56
|
||||
.circular-label {
|
||||
background: #B8C7E0;
|
||||
}
|
||||
|
||||
&--white {
|
||||
background: #fff;
|
||||
color: #5e6d7a;
|
||||
.circular-label.e2ee {
|
||||
align-items: center;
|
||||
background: #76CF9C;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
svg {
|
||||
fill: #5e6d7a;
|
||||
}
|
||||
.circular-label.file {
|
||||
background: #FF5630;
|
||||
}
|
||||
|
||||
.circular-label.local-rec {
|
||||
background: #FF5630;
|
||||
}
|
||||
|
||||
.circular-label.stream {
|
||||
background: #0065FF;
|
||||
}
|
||||
|
||||
.circular-label.insecure {
|
||||
background: $defaultWarningColor;
|
||||
}
|
||||
|
||||
.recording-label.center-message {
|
||||
background: $videoStateIndicatorBackground;
|
||||
bottom: 50%;
|
||||
display: block;
|
||||
left: 50%;
|
||||
padding: 10px;
|
||||
position: fixed;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: $centeredVideoLabelZ;
|
||||
}
|
||||
}
|
||||
|
||||
.label-text-with-icon {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.participants-count {
|
||||
cursor: pointer;
|
||||
}
|
||||
.circular-label {
|
||||
background: $videoStateIndicatorBackground;
|
||||
border-radius: 50%;
|
||||
box-sizing: border-box;
|
||||
cursor: default;
|
||||
font-size: 13px;
|
||||
height: $videoStateIndicatorSize;
|
||||
line-height: $videoStateIndicatorSize;
|
||||
text-align: center;
|
||||
min-width: $videoStateIndicatorSize;
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
.participants-count {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
color: #5e6d7a;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
margin-left: 16px;
|
||||
padding: 4px 8px;
|
||||
pointer-events: auto;
|
||||
|
||||
&-number {
|
||||
margin-right: 8px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&-icon {
|
||||
background: url('../images/user-groups.svg');
|
||||
background-repeat: no-repeat;
|
||||
display: inline-block;
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,60 +1,35 @@
|
||||
.subject {
|
||||
box-sizing: border-box;
|
||||
color: #fff;
|
||||
margin-top: 20px;
|
||||
position: absolute;
|
||||
top: -120px;
|
||||
transition: top .3s ease-in;
|
||||
height: 95px;
|
||||
width: 100%;
|
||||
z-index: $zindex3;
|
||||
pointer-events: none;
|
||||
position: absolute;
|
||||
padding: 25px 140px 0 140px;
|
||||
text-align: center;
|
||||
font-size: 17px;
|
||||
color: #fff;
|
||||
z-index: $zindex10;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
box-sizing: border-box;
|
||||
white-space: nowrap;
|
||||
|
||||
&.visible {
|
||||
top: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.subject-info-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
max-width: calc(100% - 280px);
|
||||
margin: 0 auto;
|
||||
|
||||
&--full-width {
|
||||
max-width: 100%;
|
||||
&.gradient {
|
||||
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0));
|
||||
}
|
||||
|
||||
@media (max-width: 500px) {
|
||||
flex-wrap: wrap;
|
||||
max-width: 100%;
|
||||
&-text {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&-conference-timer {
|
||||
display: block;
|
||||
font-size: 15px;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
.subject-info {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-bottom: 4px;
|
||||
max-width: 80%;
|
||||
height: 28px;
|
||||
}
|
||||
|
||||
.subject-text {
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
border-radius: 3px 0px 0px 3px;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
padding: 2px 16px;
|
||||
height: 24px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.subject-timer {
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
border-radius: 0px 3px 3px 0px;
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
line-height: 16px;
|
||||
min-width: 34px;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ $flagsImagePath: "../images/";
|
||||
@import 'videolayout_default';
|
||||
@import 'notice';
|
||||
@import 'subject';
|
||||
@import 'participants-count';
|
||||
@import 'popup_menu';
|
||||
@import 'recording';
|
||||
@import 'login_menu';
|
||||
|
||||
@@ -47,7 +47,6 @@
|
||||
|
||||
.language-settings {
|
||||
max-width: 50%;
|
||||
width: 35%;
|
||||
}
|
||||
|
||||
.calendar-tab {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
.virtual-background-dialog {
|
||||
display: inline-grid;
|
||||
grid-template-columns: auto auto auto auto auto auto auto auto;
|
||||
grid-template-columns: auto auto auto auto auto auto auto;
|
||||
max-width: 370px;
|
||||
cursor: pointer;
|
||||
.thumbnail {
|
||||
@@ -51,10 +51,6 @@
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.modal-dialog-form .virtual-background-loading {
|
||||
overflow: hidden;
|
||||
}
|
||||
.file-upload-btn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
1
debian/jitsi-meet-web.install
vendored
1
debian/jitsi-meet-web.install
vendored
@@ -1,5 +1,6 @@
|
||||
interface_config.js /usr/share/jitsi-meet/
|
||||
logging_config.js /usr/share/jitsi-meet/
|
||||
*.json /usr/share/jitsi-meet/
|
||||
*.html /usr/share/jitsi-meet/
|
||||
*.ico /usr/share/jitsi-meet/
|
||||
libs /usr/share/jitsi-meet/
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.33331 2C6.28101 2 7.09675 2.56499 7.46207 3.37651C7.00766 3.45023 6.58406 3.61583 6.21095 3.85361C6.04111 3.54356 5.71176 3.33333 5.33331 3.33333C4.78103 3.33333 4.33331 3.78105 4.33331 4.33333C4.33331 4.75895 4.59921 5.12246 4.97395 5.26682C4.77672 5.69245 4.66665 6.16671 4.66665 6.66667L4.66678 6.6967C3.12249 6.85332 2.66665 7.65415 2.66665 9.83333C2.66665 9.89666 2.66835 9.95222 2.67088 10H3.13441C2.977 10.3982 2.86114 10.8423 2.7841 11.3333H2.33331C1.66665 11.3333 1.33331 10.8333 1.33331 9.83333C1.33331 7.60559 1.88097 6.20498 3.39417 5.63152C3.14521 5.26038 2.99998 4.81382 2.99998 4.33333C2.99998 3.04467 4.04465 2 5.33331 2ZM9.78901 3.85361C9.4159 3.61583 8.9923 3.45023 8.53788 3.37651C8.90321 2.56499 9.71895 2 10.6666 2C11.9553 2 13 3.04467 13 4.33333C13 4.81382 12.8547 5.26038 12.6058 5.63152C14.119 6.20498 14.6666 7.60559 14.6666 9.83333C14.6666 10.8333 14.3333 11.3333 13.6666 11.3333H13.2159C13.1388 10.8423 13.023 10.3982 12.8656 10H13.3291C13.3316 9.95222 13.3333 9.89666 13.3333 9.83333C13.3333 7.65415 12.8775 6.85332 11.3332 6.6967L11.3333 6.66667C11.3333 6.1667 11.2232 5.69245 11.026 5.26682C11.4008 5.12246 11.6666 4.75895 11.6666 4.33333C11.6666 3.78105 11.2189 3.33333 10.6666 3.33333C10.2882 3.33333 9.95885 3.54356 9.78901 3.85361ZM4.49998 14.6667C3.7222 14.6667 3.33331 14.1111 3.33331 13C3.33331 10.4598 4.0062 8.8875 5.87888 8.28308C5.5366 7.83462 5.33331 7.27438 5.33331 6.66667C5.33331 5.19391 6.52722 4 7.99998 4C9.47274 4 10.6666 5.19391 10.6666 6.66667C10.6666 7.27438 10.4634 7.83462 10.1211 8.28308C11.9938 8.8875 12.6666 10.4598 12.6666 13C12.6666 14.1111 12.2778 14.6667 11.5 14.6667H4.49998ZM9.33331 6.66667C9.33331 7.40305 8.73636 8 7.99998 8C7.2636 8 6.66665 7.40305 6.66665 6.66667C6.66665 5.93029 7.2636 5.33333 7.99998 5.33333C8.73636 5.33333 9.33331 5.93029 9.33331 6.66667ZM11.3333 13C11.3333 13.1426 11.3252 13.2536 11.3152 13.3333H4.68477C4.67476 13.2536 4.66665 13.1426 4.66665 13C4.66665 10.1957 5.42021 9.33333 7.99998 9.33333C10.5797 9.33333 11.3333 10.1957 11.3333 13Z" />
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.33331 2C6.28101 2 7.09675 2.56499 7.46207 3.37651C7.00766 3.45023 6.58406 3.61583 6.21095 3.85361C6.04111 3.54356 5.71176 3.33333 5.33331 3.33333C4.78103 3.33333 4.33331 3.78105 4.33331 4.33333C4.33331 4.75895 4.59921 5.12246 4.97395 5.26682C4.77672 5.69245 4.66665 6.16671 4.66665 6.66667L4.66678 6.6967C3.12249 6.85332 2.66665 7.65415 2.66665 9.83333C2.66665 9.89666 2.66835 9.95222 2.67088 10H3.13441C2.977 10.3982 2.86114 10.8423 2.7841 11.3333H2.33331C1.66665 11.3333 1.33331 10.8333 1.33331 9.83333C1.33331 7.60559 1.88097 6.20498 3.39417 5.63152C3.14521 5.26038 2.99998 4.81382 2.99998 4.33333C2.99998 3.04467 4.04465 2 5.33331 2ZM9.78901 3.85361C9.4159 3.61583 8.9923 3.45023 8.53788 3.37651C8.90321 2.56499 9.71895 2 10.6666 2C11.9553 2 13 3.04467 13 4.33333C13 4.81382 12.8547 5.26038 12.6058 5.63152C14.119 6.20498 14.6666 7.60559 14.6666 9.83333C14.6666 10.8333 14.3333 11.3333 13.6666 11.3333H13.2159C13.1388 10.8423 13.023 10.3982 12.8656 10H13.3291C13.3316 9.95222 13.3333 9.89666 13.3333 9.83333C13.3333 7.65415 12.8775 6.85332 11.3332 6.6967L11.3333 6.66667C11.3333 6.1667 11.2232 5.69245 11.026 5.26682C11.4008 5.12246 11.6666 4.75895 11.6666 4.33333C11.6666 3.78105 11.2189 3.33333 10.6666 3.33333C10.2882 3.33333 9.95885 3.54356 9.78901 3.85361ZM4.49998 14.6667C3.7222 14.6667 3.33331 14.1111 3.33331 13C3.33331 10.4598 4.0062 8.8875 5.87888 8.28308C5.5366 7.83462 5.33331 7.27438 5.33331 6.66667C5.33331 5.19391 6.52722 4 7.99998 4C9.47274 4 10.6666 5.19391 10.6666 6.66667C10.6666 7.27438 10.4634 7.83462 10.1211 8.28308C11.9938 8.8875 12.6666 10.4598 12.6666 13C12.6666 14.1111 12.2778 14.6667 11.5 14.6667H4.49998ZM9.33331 6.66667C9.33331 7.40305 8.73636 8 7.99998 8C7.2636 8 6.66665 7.40305 6.66665 6.66667C6.66665 5.93029 7.2636 5.33333 7.99998 5.33333C8.73636 5.33333 9.33331 5.93029 9.33331 6.66667ZM11.3333 13C11.3333 13.1426 11.3252 13.2536 11.3152 13.3333H4.68477C4.67476 13.2536 4.66665 13.1426 4.66665 13C4.66665 10.1957 5.42021 9.33333 7.99998 9.33333C10.5797 9.33333 11.3333 10.1957 11.3333 13Z" fill="#5E6D7A"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.2 KiB |
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>21.1.0</string>
|
||||
<string>21.0.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSExtension</key>
|
||||
|
||||
@@ -88,7 +88,6 @@ static const NSInteger kBufferMaxLenght = 10 * 1024;
|
||||
CGFloat scaleFactor = 2;
|
||||
size_t width = CVPixelBufferGetWidth(imageBuffer)/scaleFactor;
|
||||
size_t height = CVPixelBufferGetHeight(imageBuffer)/scaleFactor;
|
||||
CGImagePropertyOrientation orientation = ((__bridge NSNumber*)CMGetAttachment(sampleBuffer, (__bridge CFStringRef)RPVideoSampleOrientationKey , NULL)).unsignedIntValue;
|
||||
|
||||
CGAffineTransform scaleTransform = CGAffineTransformMakeScale(1/scaleFactor, 1/scaleFactor);
|
||||
NSData *bufferData = [self jpegDataFromPixelBuffer:imageBuffer withScaling:scaleTransform];
|
||||
@@ -100,7 +99,6 @@ static const NSInteger kBufferMaxLenght = 10 * 1024;
|
||||
CFHTTPMessageSetHeaderFieldValue(httpResponse, (__bridge CFStringRef)@"Content-Length", (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", bufferData.length]);
|
||||
CFHTTPMessageSetHeaderFieldValue(httpResponse, (__bridge CFStringRef)@"Buffer-Width", (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", width]);
|
||||
CFHTTPMessageSetHeaderFieldValue(httpResponse, (__bridge CFStringRef)@"Buffer-Height", (__bridge CFStringRef)[NSString stringWithFormat:@"%ld", height]);
|
||||
CFHTTPMessageSetHeaderFieldValue(httpResponse, (__bridge CFStringRef)@"Buffer-Orientation", (__bridge CFStringRef)[NSString stringWithFormat:@"%u", orientation]);
|
||||
|
||||
CFHTTPMessageSetBody(httpResponse, (__bridge CFDataRef)bufferData);
|
||||
|
||||
|
||||
@@ -35,7 +35,6 @@
|
||||
|
||||
jitsiMeet.defaultConferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
|
||||
[builder setFeatureFlag:@"resolution" withValue:@(360)];
|
||||
[builder setFeatureFlag:@"ios.screensharing.enabled" withBoolean:YES];
|
||||
builder.serverURL = [NSURL URLWithString:@"https://meet.jit.si"];
|
||||
builder.welcomePageEnabled = YES;
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>21.1.0</string>
|
||||
<string>21.0.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>21.1.0</string>
|
||||
<string>21.0.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>21.1.0</string>
|
||||
<string>21.0.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>CLKComplicationPrincipalClass</key>
|
||||
|
||||
@@ -9,15 +9,6 @@ platform :ios do
|
||||
# Make sure we are on a clean tree
|
||||
ensure_git_status_clean
|
||||
|
||||
# Connect to Apple Store Connect
|
||||
app_store_connect_api_key(
|
||||
key_id: ENV["ASC_KEY_ID"],
|
||||
issuer_id: ENV["ASC_ISSUER_ID"],
|
||||
key_content: ENV["ASC_KEY_CONTENT"],
|
||||
duration: 1200,
|
||||
in_house: false
|
||||
)
|
||||
|
||||
# Set the app identifier
|
||||
update_app_identifier(
|
||||
xcodeproj: "app/app.xcodeproj",
|
||||
@@ -103,24 +94,12 @@ platform :ios do
|
||||
uses_non_exempt_encryption: false
|
||||
)
|
||||
|
||||
# Upload dSYMs to Crashlytics
|
||||
download_dsyms
|
||||
upload_symbols_to_crashlytics
|
||||
|
||||
# Cleanup
|
||||
clean_build_artifacts
|
||||
reset_git_repo(skip_clean: true)
|
||||
end
|
||||
|
||||
lane :refresh_dsyms do
|
||||
# Connect to Apple Store Connect
|
||||
app_store_connect_api_key(
|
||||
key_id: ENV["ASC_KEY_ID"],
|
||||
issuer_id: ENV["ASC_ISSUER_ID"],
|
||||
key_content: ENV["ASC_KEY_CONTENT"],
|
||||
duration: 1200,
|
||||
in_house: false
|
||||
)
|
||||
|
||||
# Upload dSYMs to Crashlytics
|
||||
download_dsyms(min_version: ENV["DSYMS_MIN_VERSION"]) # Download dSYM files from iTC
|
||||
upload_symbols_to_crashlytics # Upload them to Crashlytics
|
||||
clean_build_artifacts # Delete the local dSYM files
|
||||
end
|
||||
end
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.4.0</string>
|
||||
<string>3.2.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -95,7 +95,7 @@
|
||||
*
|
||||
* The `data` dictionary contains `message`, `senderId` and `isPrivate` keys.
|
||||
*/
|
||||
- (void)chatMessageReceived:(NSDictionary *)data;
|
||||
- (void)chatMessaageReceived:(NSDictionary *)data;
|
||||
|
||||
/**
|
||||
* Called when the chat dialog is displayed/hidden.
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
{
|
||||
"en": "अंग्रेज़ी",
|
||||
"hi": "हिन्दी",
|
||||
"mr":"मराठी",
|
||||
"ml": "मलयालम",
|
||||
"enGB": "अंग्रेज़ी (UK)",
|
||||
"af": "Afrikaans",
|
||||
"ar": "Arabic",
|
||||
"bg": "Bulgarian",
|
||||
"ca": "Catalan",
|
||||
"cs": "Czech",
|
||||
"da": "Danish",
|
||||
"de": "German",
|
||||
"el": "Greek",
|
||||
"eo": "Esperanto",
|
||||
"es": "Spanish",
|
||||
"esUS": "Spanish (Latin America)",
|
||||
"et": "Estonian",
|
||||
"eu": "Basque",
|
||||
"fi": "Finnish",
|
||||
"fr": "French",
|
||||
"frCA": "French (Canadian)",
|
||||
"he": "Hebrew",
|
||||
"hr": "Croatian",
|
||||
"hu": "Hungarian",
|
||||
"hy": "Armenian",
|
||||
"id": "Indonesian",
|
||||
"it": "Italian",
|
||||
"ja": "Japanese",
|
||||
"kab": "Kabyle",
|
||||
"ko": "Korean",
|
||||
"lt": "Lithuanian",
|
||||
"lv": "Latvian",
|
||||
"nl": "Dutch",
|
||||
"oc": "Occitan",
|
||||
"fa": "Persian",
|
||||
"pl": "Polish",
|
||||
"pt": "Portuguese",
|
||||
"ptBR": "Portuguese (Brazil)",
|
||||
"ru": "Russian",
|
||||
"ro": "Romanian",
|
||||
"sc": "Sardinian",
|
||||
"sk": "Slovak",
|
||||
"sl": "Slovenian",
|
||||
"sr": "Serbian",
|
||||
"sv": "Swedish",
|
||||
"th": "Thailand",
|
||||
"tr": "Turkish",
|
||||
"uk": "Ukrainian",
|
||||
"vi": "Vietnamese",
|
||||
"zhCN": "Chinese (China)",
|
||||
"zhTW": "Chinese (Taiwan)"
|
||||
}
|
||||
@@ -32,9 +32,7 @@
|
||||
"lv": "Leton",
|
||||
"nl": "Neerlandés",
|
||||
"oc": "Occitan",
|
||||
"fa": "Persa",
|
||||
"pl": "Polonés",
|
||||
"pt": "Portugués",
|
||||
"ptBR": "Portugués (Brasil)",
|
||||
"ru": "Rus",
|
||||
"ro": "Romanian",
|
||||
@@ -50,3 +48,5 @@
|
||||
"zhCN": "Chinés (China)",
|
||||
"zhTW": "Chinés (Taiwan)"
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
"fr": "French",
|
||||
"frCA": "French (Canadian)",
|
||||
"he": "Hebrew",
|
||||
"hi": "Hindi",
|
||||
"mr":"Marathi",
|
||||
"hr": "Croatian",
|
||||
"hu": "Hungarian",
|
||||
@@ -35,7 +34,6 @@
|
||||
"oc": "Occitan",
|
||||
"fa": "Persian",
|
||||
"pl": "Polish",
|
||||
"pt": "Portuguese",
|
||||
"ptBR": "Portuguese (Brazil)",
|
||||
"ru": "Russian",
|
||||
"ro": "Romanian",
|
||||
|
||||
@@ -562,8 +562,8 @@
|
||||
"linkCopied": "Link in die Zwischenablage kopiert",
|
||||
"lookGood": "Ihr Mikrofon scheint zu funktionieren.",
|
||||
"or": "oder",
|
||||
"premeeting": "Vorschau",
|
||||
"showScreen": "Konferenzvorschau aktivieren",
|
||||
"premeeting": "Vorraum",
|
||||
"showScreen": "Konferenzvorraum aktivieren",
|
||||
"startWithPhone": "Mit Telefonaudio starten",
|
||||
"screenSharingError": "Fehler bei Bildschirmfreigabe:",
|
||||
"videoOnlyError": "Videofehler:",
|
||||
|
||||
@@ -201,9 +201,9 @@
|
||||
"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.",
|
||||
"e2eeLabel": "Activer le chiffrement de Bout-en-Bout",
|
||||
"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.",
|
||||
"e2eeDescription": "Le cryptage de Bout-en-Bout est actuellement EXPERIMENTAL. Veuillez garder à l'esprit que l'activation du cryptage 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.",
|
||||
"e2eeLabel": "Activer le cryptage de Bout-en-Bout",
|
||||
"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 cryptage, ils ne pourront ni vous voir, ni vous entendre.",
|
||||
"enterDisplayName": "Merci de saisir votre nom ici",
|
||||
"error": "Erreur",
|
||||
"externalInstallationMsg": "Vous devez installer notre extension de partage de bureau.",
|
||||
@@ -326,7 +326,7 @@
|
||||
"title": "Document partagé"
|
||||
},
|
||||
"e2ee": {
|
||||
"labelToolTip": "L'audio et la vidéo de cette conférence sont chiffrés de Bout-en-Bout"
|
||||
"labelToolTip": "L'audio et la vidéo de cette conférence sont cryptés de Bout-en-Bout"
|
||||
},
|
||||
"embedMeeting": {
|
||||
"title": "Intégrer cette réunion"
|
||||
@@ -757,7 +757,7 @@
|
||||
"documentClose": "Fermer le document partagé",
|
||||
"documentOpen": "Ouvrir le document partagé",
|
||||
"download": "Télécharger nos applications",
|
||||
"e2ee": "Chiffrement de Bout-en-Bout",
|
||||
"e2ee": "Cryptage de Bout-en-Bout",
|
||||
"embedMeeting": "Intégrer la réunion",
|
||||
"enterFullScreen": "Afficher en plein écran",
|
||||
"enterTileView": "Accéder au mode mosaïque",
|
||||
|
||||
@@ -718,7 +718,7 @@
|
||||
"join": "Toucher pour rejoindre",
|
||||
"roomname": "Entrer le nom de la salle"
|
||||
},
|
||||
"appDescription": "Profitez de la conversation vidéo avec toute votre équipe. Allez-y, invitez tous ceux que vous connaissez. {{app}} est une solution 100 % libre de conférence vidéo entièrement chiffrée que vous pouvez utiliser en tout temps et gratuitement, sans avoir besoin de compte.",
|
||||
"appDescription": "Profitez de la conversation vidéo avec toute votre équipe. Allez-y, invitez tous ceux que vous connaissez. {{app}} est une solution 100 % libre de conférence vidéo entièrement cryptée que vous pouvez utiliser en tout temps et gratuitement, sans avoir besoin de compte.",
|
||||
"audioVideoSwitch": {
|
||||
"audio": "Téléphone",
|
||||
"video": "Vidéo"
|
||||
|
||||
@@ -1,972 +0,0 @@
|
||||
{
|
||||
"addPeople": {
|
||||
"add": "आमंत्रित करें",
|
||||
"addContacts": "संपर्क सूची से आमंत्रित करे",
|
||||
"copyInvite": "मीटिंग के आमंत्रण कि प्रतिलिपि बनाये",
|
||||
"copyLink": "मीटिंग कि लिंक कि प्रतिलिपि बनाये",
|
||||
"copyStream": "सीधे प्रसारण कि लिंक कि प्रीतिलिपि बनाये",
|
||||
"countryNotSupported": "अभी हम इस गतव्य के लिये सक्षम नही है ।",
|
||||
"countryReminder": "यू.एस. के बाहर से काल कर रहे है तो कृपया सुनिश्चित करे कि अपने देश के कोड़ से प्रारंभ कर रहे है !",
|
||||
"defaultEmail": "अपना ई-मेल पता लिखें",
|
||||
"disabled": "आप अन्य लोगों को आमंत्रित नही कर सकते",
|
||||
"failedToAdd": "प्रतिभागियों को जोड़ने में विफल",
|
||||
"footerText": "बाहर डालय करना प्रतिबंधित है",
|
||||
"googleEmail": "गूगल ई-मेल",
|
||||
"inviteMoreHeader": "मीटिंग मे केवल आप ही हैं",
|
||||
"inviteMoreMailSubject": "मीटिंग में शामिल हो {{appName}} ",
|
||||
"inviteMorePrompt": "और लोगों को आमंत्रित करें",
|
||||
"linkCopied": "लिंक कि प्रतिलिपि बनायी गयी",
|
||||
"loading": "लोगों को उनके मोबाइल नंबर से खोजा जा रहा हैं ",
|
||||
"loadingNumber": "मोबाइल नम्बर कि जांच हो रही है ",
|
||||
"loadingPeople": "लोगों को आमंत्रित करने के लिए खोजा जा रहा हैं ",
|
||||
"noResults": "कोई मिलान खोज परिणाम नहीं",
|
||||
"noValidNumbers": "कृपया एक फ़ोन नंबर दर्ज करें",
|
||||
"outlookEmail": "आउटलुक ईमेल",
|
||||
"searchNumbers": "फ़ोन नंबर जोड़ें",
|
||||
"searchPeople": "लोगों को खोजें",
|
||||
"searchPeopleAndNumbers": "लोगों को खोजें या उनके फ़ोन नंबर जोड़ें",
|
||||
"shareInvite": "मीटिंग आमंत्रण साझा करे ",
|
||||
"shareLink": "दूसरों को आमंत्रित करने के लिए मीटिंग लिंक साझा करें",
|
||||
"shareStream": "सीधे प्रसारण लिंक साझा करें",
|
||||
"sip": "SIP: {{address}}",
|
||||
"telephone": "टेलीफोन: {{number}}",
|
||||
"title": "लोगों को इस बैठक में आमंत्रित करें",
|
||||
"yahooEmail": "याहू ईमेल"
|
||||
},
|
||||
"audioDevices": {
|
||||
"bluetooth": "ब्लूटूथ",
|
||||
"headphones": "हेडफ़ोन",
|
||||
"phone": "फ़ोन",
|
||||
"speaker": "स्पीकर",
|
||||
"none": "कोई ऑडियो डिवाइस उपलब्ध नहीं"
|
||||
},
|
||||
"audioOnly": {
|
||||
"audioOnly": "लो बैंडविड्थ"
|
||||
},
|
||||
"calendarSync": {
|
||||
"addMeetingURL": "एक मीटिंग लिंक जोड़ें",
|
||||
"confirmAddLink": "क्या आप इस इवेंट में एक Jitsi लिंक जोड़ना चाहते हैं?",
|
||||
"error": {
|
||||
"appConfiguration": "कैलेंडर एकीकरण ठीक से कॉन्फ़िगर नहीं किया गया है।",
|
||||
"generic": "एक त्रुटि हुई है। कृपया अपनी कैलेंडर सेटिंग जांचें या कैलेंडर को रीफ़्रेश करने का प्रयास करें।",
|
||||
"notSignedIn": "कैलेंडर ईवेंट देखने के लिए प्रमाणित करते समय एक त्रुटि हुई। कृपया अपनी कैलेंडर सेटिंग जांचें और फिर से लॉग इन करने का प्रयास करें।"
|
||||
},
|
||||
"join": "जुड़े",
|
||||
"joinTooltip": "मीटिंग में शामिल हों",
|
||||
"nextMeeting": "अगली मीटिंग",
|
||||
"noEvents": "कोई आगामी कार्यक्रम निर्धारित नहीं हैं।",
|
||||
"ongoingMeeting": "चल रही बैठक",
|
||||
"permissionButton": "सेटिंग्स खोले ",
|
||||
"permissionMessage": "ऐप में आपकी मीटिंग देखने के लिए कैलेंडर की अनुमति आवश्यक है।",
|
||||
"refresh": "कैलेंडर रीफ़्रेश करें",
|
||||
"today": "आज"
|
||||
},
|
||||
"chat": {
|
||||
"enter": "चैट रूम में प्रवेश करें",
|
||||
"error": "त्रुटि: आपका संदेश नहीं भेजा गया । कारण: {{error}}",
|
||||
"fieldPlaceHolder": "अपना संदेश यहां लिखें",
|
||||
"messagebox": "एक संदेश टाइप करें",
|
||||
"messageTo": "{{recipient}} के लिए निजी संदेश",
|
||||
"noMessagesMessage": "अभी तक मीटिंग में कोई संदेश नहीं आया है। वार्तालाप प्रारंभ करें!",
|
||||
"nickname": {
|
||||
"popover": "एक उपनाम चुनें",
|
||||
"title": "चैट का उपयोग करने के लिए एक उपनाम दर्ज करें"
|
||||
},
|
||||
"privateNotice": "{{recipient}} के लिए निजी संदेश",
|
||||
"title": "चैट",
|
||||
"you": "आप"
|
||||
},
|
||||
"chromeExtensionBanner": {
|
||||
"installExtensionText": ",गूगल कैलेंडर और ऑफिस 365 एकीकरण के लिए एक्सटेंशन इंस्टॉल करें",
|
||||
"buttonText": "क्रोम एक्सटेंशन इंस्टॉल करें",
|
||||
"dontShowAgain": "मुझे यह फिर से न दिखाएं"
|
||||
},
|
||||
"connectingOverlay": {
|
||||
"joiningRoom": "आपको आपकी मीटिंग से कनेक्ट किया जा रहा है ..."
|
||||
},
|
||||
"connection": {
|
||||
"ATTACHED": "संलग्न",
|
||||
"AUTHENTICATING": "प्रमाणीकरण",
|
||||
"AUTHFAIL": "प्रमाणीकरण विफल",
|
||||
"CONNECTED": "जुड़े हुए हैं",
|
||||
"CONNECTING": "जोड़ा जा रहा हैं ",
|
||||
"CONNFAIL": "नहीं जोड़ा जा सका ",
|
||||
"DISCONNECTED": "संपर्क विफ़ल ",
|
||||
"DISCONNECTING": "संपर्क हटाया जा रहा ",
|
||||
"ERROR": "त्रुटि",
|
||||
"FETCH_SESSION_ID": "सत्र-आईडी प्राप्त की जा रही हैं ...",
|
||||
"GET_SESSION_ID_ERROR": "सत्र-आईडी त्रुटि प्राप्त करें: {{code}}",
|
||||
"GOT_SESSION_ID": "सत्र-आईडी प्राप्त की जा रही हैं... पूर्ण",
|
||||
"LOW_BANDWIDTH": "बैंडविड्थ को बचाने के लिए {{displayName}} का वीडियो बंद कर दिया गया है"
|
||||
},
|
||||
"connectionindicator": {
|
||||
"address": "पता:",
|
||||
"audio_ssrc": "ऑडियो एस.आर.सी.सी.:",
|
||||
"bandwidth": "अनुमानित बैंडविड्थ:",
|
||||
"bitrate": "बिटरेट:",
|
||||
"bridgeCount": "सर्वर गणना: ",
|
||||
"codecs": "कोडेक (ए/वी): ",
|
||||
"connectedTo": "से जुड़ा हुआ है :",
|
||||
"e2e_rtt": "ई.2ई. आर.टी.टी.:",
|
||||
"framerate": "फ्रेम दर:",
|
||||
"less": "कम दिखाएं",
|
||||
"localaddress": "स्थानीय पता:",
|
||||
"localaddress_plural": "स्थानीय पते:",
|
||||
"localport": "local port:",
|
||||
"localport_plural": "Local ports:",
|
||||
"maxEnabledResolution": "send max",
|
||||
"more": "और दिखाएं",
|
||||
"packetloss": "पैकेट लॉस:",
|
||||
"quality": {
|
||||
"good": "अच्छी",
|
||||
"inactive": "निष्क्रिय",
|
||||
"lost": "गया",
|
||||
"nonoptimal": "अयुक्ततम",
|
||||
"poor": "घटिया"
|
||||
},
|
||||
"remoteaddress": "रिमोट एड्रेस:",
|
||||
"remoteaddress_plural": "रिमोट एड्रेसेस:",
|
||||
"remoteport": "रिमोट port:",
|
||||
"remoteport_plural": "रिमोट ports:",
|
||||
"resolution": "रेसोलुशन:",
|
||||
"savelogs": "लॉग सहेजे",
|
||||
"participant_id": "प्रतिभागी आईडी:",
|
||||
"status": "सम्पर्क:",
|
||||
"transport": "ट्रांसपोर्ट :",
|
||||
"transport_plural": "ट्रांसपोर्ट्स:",
|
||||
"video_ssrc": "वीडियो एस.आर.सी.सी.:"
|
||||
},
|
||||
"dateUtils": {
|
||||
"earlier": "पिछला कल",
|
||||
"today": "आज",
|
||||
"yesterday": "अगला कल"
|
||||
},
|
||||
"deepLinking": {
|
||||
"appNotInstalled": "आपको अपने फ़ोन पर इस मीटिंग में शामिल होने के लिए {{app}} मोबाइल ऐप की आवश्यकता है। ",
|
||||
"description": "हमने आपकी मीटिंग {{app}} डेस्कटॉप ऐप में लॉन्च करने की कोशिश की। कुछ नहीं हुआ? फिर से कोशिश करें या {{app}} वेब ऐप में लॉन्च करें।",
|
||||
"descriptionWithoutWeb": "हमने आपकी मीटिंग {{app}} डेस्कटॉप ऐप में लॉन्च करने की कोशिश की। कुछ नहीं हुआ?",
|
||||
"downloadApp": "एप्लिकेशन डाउनलोड करें",
|
||||
"ifDoNotHaveApp": "यदि आपके पास अभी तक ऐप नहीं है:",
|
||||
"ifHaveApp": "यदि आपके पास पहले से ही ऐप है:",
|
||||
"joinInApp": "ऐप का उपयोग करके इस मीटिंग में शामिल हों",
|
||||
"launchWebButton": "वेब में लॉन्च करे",
|
||||
"title": "{{app}} में आपकी मीटिंग शुरू की जा रही हैं ...",
|
||||
"tryAgainButton": "डेस्कटॉप में फिर से प्रयास करें"
|
||||
},
|
||||
"defaultLink": "उदाहरण {{url}}",
|
||||
"defaultNickname": "उदा. सतीष कुमार",
|
||||
"deviceError": {
|
||||
"cameraError": "कैमरे को एक्सेस करने में विफल",
|
||||
"cameraPermission": "कैमरा अनुमति प्राप्त करने में त्रुटि",
|
||||
"microphoneError": "माइक्रोफ़ोन को एक्सेस करने में विफल",
|
||||
"microphonePermission": "माइक्रोफ़ोन अनुमति प्राप्त करने में त्रुटि"
|
||||
},
|
||||
"deviceSelection": {
|
||||
"noPermission": "अनुमति नहीं दी गई",
|
||||
"previewUnavailable": "पूर्वदर्शन अनुपलब्ध",
|
||||
"selectADevice": "डिवाइस का चयन करें",
|
||||
"testAudio": "टेस्ट साउंड प्ले करें"
|
||||
},
|
||||
"dialog": {
|
||||
"accessibilityLabel": {
|
||||
"liveStreaming": "सीधा प्रसारण"
|
||||
},
|
||||
"add": "जोड़ें",
|
||||
"allow": "अनुमति दें",
|
||||
"alreadySharedVideoMsg": "एक अन्य प्रतिभागी पहले से ही वीडियो साझा कर रहा है। यह सम्मेलन एक समय में केवल एक साझा की अनुमति देता है।",
|
||||
"alreadySharedVideoTitle": "एक समय में केवल एक साझा वीडियो की अनुमति है",
|
||||
"applicationWindow": "एप्लिकेशन विंडो",
|
||||
"authenticationRequired": "प्रमाणीकरण आवश्यक है",
|
||||
"Back": "पीछे जाए",
|
||||
"cameraConstraintFailedError": "Your camera does not satisfy some of the required constraints.",
|
||||
"cameraNotFoundError": "कैमरा नहीं मिला।",
|
||||
"cameraNotSendingData": "हम आपके कैमरे का उपयोग करने में असमर्थ हैं। कृपया जांचें कि क्या कोई अन्य एप्लिकेशन इस डिवाइस का उपयोग तो नहीं कर रहा है, सेटिंग मेनू से किसी अन्य डिवाइस का चयन करें या एप्लिकेशन को फिर से लोड करने का प्रयास करें।",
|
||||
"cameraNotSendingDataTitle": "कैमरा उपयोग करने में असमर्थ",
|
||||
"cameraPermissionDeniedError": "आपने अपने कैमरे का उपयोग करने की अनुमति नहीं दी है। आप अभी भी सम्मेलन में शामिल हो सकते हैं लेकिन अन्य लोग आपको नहीं देख सकेंगे। इसे ठीक करने के लिए पता बार में कैमरा बटन का उपयोग करें।",
|
||||
"cameraTimeoutError": "वीडियो स्रोत प्रारंभ नहीं किया जा सका। समय समाप्त हो गया!",
|
||||
"cameraUnknownError": "अज्ञात कारण की वजह से कैमरे का उपयोग नहीं किया जा सकता है।",
|
||||
"cameraUnsupportedResolutionError": "आपका कैमरा आवश्यक वीडियो रिज़ॉल्यूशन का समर्थन नहीं करता है।",
|
||||
"Cancel": "रद्द करें",
|
||||
"close": "बंद करें",
|
||||
"conferenceDisconnectMsg": "आप अपने नेटवर्क कनेक्शन की जांच कर सकते है। . {{seconds}} सेकंड में पुनः कनेक्ट किया जायेंगा ...",
|
||||
"conferenceDisconnectTitle": "आपको डिस्कनेक्ट कर दिया गया है।",
|
||||
"conferenceReloadMsg": "हम इसे ठीक करने का प्रयास कर रहे हैं। {{seconds}} सेकंड में पुनः कनेक्ट कर रहे हैं ...",
|
||||
"conferenceReloadTitle": "दुर्भाग्य से, कुछ गलत हो गया।",
|
||||
"confirm": "पुष्टि करें",
|
||||
"confirmNo": "नहीं",
|
||||
"confirmYes": "हाँ",
|
||||
"connectError": "उफ़! कुछ गड़बड़ हो गई और हम सम्मेलन से जुड़ नहीं सके।",
|
||||
"connectErrorWithMsg": "उफ़! कुछ गड़बड़ हो गई और हम सम्मेलन से नहीं जुड़ सके: {{msg}}",
|
||||
"connecting": "संपर्क जोड़ा जा रहा है ",
|
||||
"contactSupport": "सहयोग के लिए संपर्क करें",
|
||||
"copied": "प्रतिलिपि बनाई गयी ",
|
||||
"copy": "प्रतिलिपि बनाये",
|
||||
"dismiss": "खारिज करें",
|
||||
"displayNameRequired": "नमस्ते! आपका नाम क्या है?",
|
||||
"done": "हो गया ",
|
||||
"e2eeDescription": "एंड-टू-एंड एन्क्रिप्शन वर्तमान में प्रयोगात्मक है। कृपया ध्यान रखें कि एंड-टू-एंड एन्क्रिप्शन को चालू करने से सर्वर-साइड सेवाएं जैसे: रिकॉर्डिंग, लाइव स्ट्रीमिंग और फोन भागीदारी निष्क्रिय । Also keep in mind that the meeting will only work for people joining from browsers with support for insertable streams.",
|
||||
"e2eeLabel": "एंड-टू-एंड एन्क्रिप्शन सक्षम करें",
|
||||
"e2eeWarning": "चेतावनी: इस मीटिंग में सभी प्रतिभागियों के पास एंड-टू-एंड एन्क्रिप्शन के लिए समक्षता नहीं है। यदि आप इसे सक्षम करते हैं तो वे आपको देखने और सुनने में सक्षम नहीं होंगे।",
|
||||
"enterDisplayName": "कृपया यहाँ अपना नाम लिखें",
|
||||
"error": "त्रुटि",
|
||||
"gracefulShutdown": "Our service is currently down for maintenance. Please try again later.",
|
||||
"grantModeratorDialog": "क्या आप वाकई इस प्रतिभागी को एक मध्यस्थ बनाना चाहते हैं?",
|
||||
"grantModeratorTitle": "मध्यस्थ स्वीकृती दे ",
|
||||
"IamHost": "मैं मेजबान हूँ",
|
||||
"incorrectRoomLockPassword": "गलत पासवर्ड",
|
||||
"incorrectPassword": "गलत उपयोगकर्ता नाम या पासवर्ड",
|
||||
"internalError": "उफ़! कुछ गड़बड़ हो गई। निम्नलिखित त्रुटि हुई: {{error}}",
|
||||
"internalErrorTitle": "आंतरिक त्रुटि",
|
||||
"kickMessage": "आप अधिक जानकारी के लिए {{participantDisplayName}} से संपर्क कर सकते हैं।",
|
||||
"kickParticipantButton": "Kick",
|
||||
"kickParticipantDialog": "क्या आप वाकई इस प्रतिभागी को निकलना चाहते हैं?",
|
||||
"kickParticipantTitle": "इस प्रतिभागी को निकाले?",
|
||||
"kickTitle": "अरे! {{participantDisplayName}} ने आपको मीटिंग से बाहर कर दिया",
|
||||
"liveStreaming": "सीधा प्रसारण",
|
||||
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "रिकॉर्डिंग सक्रिय होने के दौरान संभव नहीं है",
|
||||
"liveStreamingDisabledTooltip": "सीधा प्रसारण करना निष्क्रिय है ",
|
||||
"lockMessage": "सम्मेलन को लॉक करने में विफल।",
|
||||
"lockRoom": "मीटिंग जोड़ें $t(lockRoomPasswordUppercase)",
|
||||
"lockTitle": "लॉक फेल",
|
||||
"logoutQuestion": "क्या आप वाकई लॉगआउट और कॉन्फ्रेंस को रोकना चाहते हैं?",
|
||||
"login": "लॉग इन",
|
||||
"logoutTitle": "लॉग आउट ",
|
||||
"maxUsersLimitReached": "अधिकतम प्रतिभागियों की सीमा पूरी हो चुकी है. कृपया बैठक के मालिक से संपर्क करें या बाद में पुनः प्रयास करें!!",
|
||||
"maxUsersLimitReachedTitle": "अधिकतम प्रतिभागियों सीमा पार हो गई",
|
||||
"micConstraintFailedError": "Your microphone does not satisfy some of the required constraints.",
|
||||
"micNotFoundError": "माइक्रोफोन नहीं मिला।",
|
||||
"micNotSendingData": "अपने माइक को अनम्यूट करने और इसके स्तर को समायोजित करने के लिए अपने कंप्यूटर की सेटिंग पर जाएं",
|
||||
"micNotSendingDataTitle": "आपका माइक आपकी सिस्टम सेटिंग्स द्वारा मौन है",
|
||||
"micPermissionDeniedError": "आपने अपने माइक्रोफ़ोन का उपयोग करने की अनुमति नहीं दी है। आप अभी भी सम्मेलन में शामिल हो सकते हैं, लेकिन अन्य लोग आपको नहीं सुनेंगे। इसे ठीक करने के लिए पता बार में कैमरा बटन का उपयोग करें।",
|
||||
"micTimeoutError": "ऑडियो स्रोत प्रारंभ नहीं किया जा सका। समय समाप्त हो गया!",
|
||||
"micUnknownError": "अज्ञात कारण से माइक्रोफोन का उपयोग नहीं किया जा सकता है।",
|
||||
"muteEveryoneElseDialog": "एक बार म्यूट होने के बाद, आप उन्हें अनम्यूट नहीं कर पाएंगे, लेकिन वे किसी भी समय खुद को अनम्यूट कर सकते हैं।",
|
||||
"muteEveryoneElseTitle": "{{whom}} को छोड़कर सभी को म्यूट करें?",
|
||||
"muteEveryoneDialog": "क्या आप वाकई सभी को म्यूट करना चाहते हैं? आप उन्हें अनम्यूट नहीं कर पाएंगे, लेकिन वे किसी भी समय खुद को अनम्यूट कर सकते हैं।",
|
||||
"muteEveryoneTitle": "सभी को म्यूट करें?",
|
||||
"muteEveryoneElsesVideoDialog": "एक बार कैमरा अक्षम हो जाने पर, आप इसे वापस चालू नहीं कर पाएंगे, लेकिन वे इसे किसी भी समय वापस चालू कर सकते हैं।",
|
||||
"muteEveryoneElsesVideoTitle": "{{whom}} को छोड़कर सभी का कैमरा अक्षम करें?",
|
||||
"muteEveryonesVideoDialog": "क्या आप वाकई सभी के कैमरे को अक्षम करना चाहते हैं? आप इसे वापस चालू नहीं कर पाएंगे, लेकिन वे इसे किसी भी समय वापस चालू कर सकते हैं।",
|
||||
"muteEveryonesVideoDialogOk": "अक्षम करें",
|
||||
"muteEveryonesVideoTitle": "सभी का कैमरा अक्षम करें?",
|
||||
"muteEveryoneSelf": "अपने आप",
|
||||
"muteEveryoneStartMuted": "Everyone starts muted from now on",
|
||||
"muteParticipantBody": "आप उन्हें अनम्यूट नहीं कर पाएंगे, लेकिन वे किसी भी समय अनम्यूट कर सकते हैं।",
|
||||
"muteParticipantButton": "म्यूट",
|
||||
"muteParticipantDialog": "क्या आप वाकई इस प्रतिभागी को म्यूट करना चाहते हैं? आप उन्हें अनम्यूट नहीं कर पाएंगे, लेकिन वे किसी भी समय खुद को अनम्यूट कर सकते हैं।",
|
||||
"muteParticipantTitle": "इस प्रतिभागी को म्यूट करें?",
|
||||
"muteParticipantsVideoButton": "कैमरा अक्षम करें",
|
||||
"muteParticipantsVideoTitle": "इस प्रतिभागी का कैमरा अक्षम करें?",
|
||||
"muteParticipantsVideoBody": "आप कैमरे को वापस चालू नहीं कर पाएंगे, लेकिन वे इसे किसी भी समय वापस चालू कर सकते हैं।",
|
||||
"Ok": "ठीक है",
|
||||
"passwordLabel": "बैठक में एक प्रतिभागी द्वारा ताला लगा दिया गया है। कृपया शामिल होने के लिए $t(lockRoomPassword) दर्ज करें।",
|
||||
"passwordNotSupported": "मीटिंग $t(lockRoomPassword) सेट करना समर्थित नहीं है।",
|
||||
"passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) समर्थित नहीं है",
|
||||
"passwordRequired": "$t(lockRoomPasswordUppercase) की आवश्यकता है",
|
||||
"popupError": "आपका ब्राउज़र इस साइट से पॉप-अप विंडो को रोक रहा है। कृपया अपने ब्राउज़र की सुरक्षा सेटिंग्स में पॉप-अप को सक्षम करें और पुनः प्रयास करें।",
|
||||
"popupErrorTitle": "पॉप-अप अवरुद्ध",
|
||||
"readMore": "अधिक",
|
||||
"recording": "रिकॉर्डिंग",
|
||||
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "संभव नहीं है जब एक लाइव स्ट्रीम सक्रिय है",
|
||||
"recordingDisabledTooltip": "रिकॉर्डिंग शुरू करना अक्षम करें.",
|
||||
"rejoinNow": "पुनः जुड़े",
|
||||
"remoteControlAllowedMessage": "{{user}} ने आपका रिमोट कंट्रोल अनुरोध स्वीकार कर लिया!",
|
||||
"remoteControlDeniedMessage": "{{user}} ने आपका रिमोट कंट्रोल अनुरोध अस्वीकार कर दिया!",
|
||||
"remoteControlErrorMessage": "{{user}}से रिमोट कंट्रोल की अनुमति का अनुरोध करते समय एक त्रुटि हुई!",
|
||||
"remoteControlRequestMessage": "क्या आप {{user}} को दूर से अपने डेस्कटॉप को नियंत्रित करने की अनुमति देंगे?",
|
||||
"remoteControlShareScreenWarning": "Note that if you press \"Allow\" you will share your screen!",
|
||||
"remoteControlStopMessage": "रिमोट कंट्रोल सत्र समाप्त हो गया!",
|
||||
"remoteControlTitle": "रिमोट डेस्कटॉप कंट्रोल",
|
||||
"Remove": "निकालें",
|
||||
"removePassword": "निकालें $t(lockRoomPassword)",
|
||||
"removeSharedVideoMsg": "क्या आप वाकई अपने साझा किए गए वीडियो को निकालना चाहते हैं?",
|
||||
"removeSharedVideoTitle": "साझा किया गया वीडियो निकालें",
|
||||
"reservationError": "Reservation system error",
|
||||
"reservationErrorMsg": "Error code: {{code}}, message: {{msg}}",
|
||||
"retry": "पुनः प्रयास करें",
|
||||
"screenSharingAudio": "Share audio",
|
||||
"screenSharingFailed": "उफ़! कुछ गड़बड़ हो गई, हम स्क्रीन शेयरिंग शुरू करने में सक्षम नहीं थे!",
|
||||
"screenSharingFailedTitle": "Screen sharing failed!",
|
||||
"screenSharingPermissionDeniedError": "उफ़! आपकी स्क्रीन शेयरिंग अनुमतियों में कुछ गड़बड़ हो गई है। कृपया पुनः लोड करें और पुनः प्रयास करें।",
|
||||
"sendPrivateMessage": "You recently received a private message. Did you intend to reply to that privately, or you want to send your message to the group?",
|
||||
"sendPrivateMessageCancel": "समूह को भेजें",
|
||||
"sendPrivateMessageOk": "निजी तौर पर भेजें",
|
||||
"sendPrivateMessageTitle": "निजी तौर पर भेजें?",
|
||||
"serviceUnavailable": "सेवा अनुपलब्ध",
|
||||
"sessTerminated": "कॉल समाप्त",
|
||||
"sessionRestarted": "Call restarted by the bridge",
|
||||
"Share": "Share",
|
||||
"shareVideoLinkError": "कृपया एक सही यूट्यूब लिंक प्रदान करें।.",
|
||||
"shareVideoTitle": "एक वीडियो साझा करें",
|
||||
"shareYourScreen": "अपनी स्क्रीन साझा करें",
|
||||
"shareYourScreenDisabled": "स्क्रीन साझाकरण अक्षम।",
|
||||
"startLiveStreaming": "लाइव स्ट्रीम प्रारंभ करें",
|
||||
"startRecording": "रिकॉर्डिंग प्रारंभ करें",
|
||||
"startRemoteControlErrorMessage": "रिमोट कंट्रोल सत्र शुरू करने की कोशिश करते समय एक त्रुटि हुई!",
|
||||
"stopLiveStreaming": "लाइव स्ट्रीम बंद करें",
|
||||
"stopRecording": "Stop recording",
|
||||
"stopRecordingWarning": "क्या आप वाकई रिकॉर्डिंग को रोकना चाहते हैं?",
|
||||
"stopStreamingWarning": "क्या आप वाकई लाइव स्ट्रीमिंग को रोकना चाहते हैं?",
|
||||
"streamKey": "Live stream key",
|
||||
"Submit": "सबमिट करें",
|
||||
"thankYou": " {{appName}} का उपयोग करने के लिए धन्यवाद!",
|
||||
"token": "टोकन",
|
||||
"tokenAuthFailed": "क्षमा करें, आपको इस कॉल में शामिल होने की अनुमति नहीं है।",
|
||||
"tokenAuthFailedTitle": "प्रमाणीकरण विफल",
|
||||
"transcribing": "प्रतिलेखन",
|
||||
"unlockRoom": "मीटिंग को $t(lockRoomPassword) निकालें",
|
||||
"user": "उपयोगकर्ता",
|
||||
"userIdentifier": "उपयोगकर्ता पहचानकर्ता",
|
||||
"userPassword": "उपयोगकर्ता पासवर्ड",
|
||||
"videoLink": "वीडियो लिंक",
|
||||
"WaitForHostMsg": "सम्मेलन <b>{{room}}</b> अभी तक शुरू नहीं हुआ है। यदि आप मेजबान हैं तो कृपया प्रमाणित करें। अन्यथा, कृपया मेजबान के आने की प्रतीक्षा करें।",
|
||||
"WaitForHostMsgWOk": "सम्मेलन <b>{{room}}</b> अभी तक शुरू नहीं हुआ है। यदि आप मेजबान हैं तो कृपया प्रमाणित करने के लिए ओके दबाएं। अन्यथा, कृपया मेजबान के आने की प्रतीक्षा करें।",
|
||||
"WaitingForHostTitle": "होस्ट की प्रतीक्षा कर रहा है ...",
|
||||
"Yes": "हाँ",
|
||||
"yourEntireScreen": "आपकी पूरी स्क्रीन"
|
||||
},
|
||||
"dialOut": {
|
||||
"statusMessage": "अब {{status}} है"
|
||||
},
|
||||
"documentSharing": {
|
||||
"title": "साझा दस्तावेज़"
|
||||
},
|
||||
"e2ee": {
|
||||
"labelToolTip": "इस कॉल पर ऑडियो और वीडियो संचार एंड-टू-एंड एन्क्रिप्टेड है"
|
||||
},
|
||||
"embedMeeting": {
|
||||
"title": "Embed this meeting"
|
||||
},
|
||||
"virtualBackground": {
|
||||
"title": "पृष्ठभूमि",
|
||||
"enableBlur": "ब्लर सक्षम करें",
|
||||
"removeBackground": "पृष्ठभूमि निकालें",
|
||||
"uploadImage": "छवि अपलोड करें",
|
||||
"pleaseWait": "कृपया प्रतीक्षा करें ...",
|
||||
"none": "कोई नहीं"
|
||||
},
|
||||
"feedback": {
|
||||
"average": "औसत",
|
||||
"bad": "बुरा",
|
||||
"detailsLabel": "इसके बारे में अधिक बताएं।",
|
||||
"good": "अच्छा",
|
||||
"rateExperience": "अपने बैठक के अनुभव को रेट करें",
|
||||
"veryBad": "बहुत बुरा",
|
||||
"veryGood": "बहुत अच्छा"
|
||||
},
|
||||
"incomingCall": {
|
||||
"answer": "उत्तर",
|
||||
"audioCallTitle": "आने वाले कॉल",
|
||||
"decline": "खारिज",
|
||||
"productLabel": "जित्सी मीट से",
|
||||
"videoCallTitle": "आने वाले वीडियो कॉल"
|
||||
},
|
||||
"info": {
|
||||
"accessibilityLabel": "जानकारी दिखाएं",
|
||||
"addPassword": "$t(lockRoomPassword)जोड़ें",
|
||||
"cancelPassword": "$t(lockRoomPassword)रद्द करें",
|
||||
"conferenceURL": "लिंक:",
|
||||
"country": "देश",
|
||||
"dialANumber": "अपनी मीटिंग में शामिल होने के लिए, इनमें से किसी एक नंबर को डायल करें और फिर पिन डालें।",
|
||||
"dialInConferenceID": "पिन:",
|
||||
"dialInNotSupported": "क्षमा करें, वर्तमान में डायल करना समर्थित नहीं है।",
|
||||
"dialInNumber": "डायल-इन:",
|
||||
"dialInSummaryError": "डायल-इन जानकारी लाने में त्रुटि कृपया बाद में पुनः प्रयास करें।",
|
||||
"dialInTollFree": "टोल फ्री",
|
||||
"genericError": "वूप्स, कुछ गलत हो गया।",
|
||||
"inviteLiveStream": "इस बैठक की लाइव स्ट्रीम देखने के लिए, इस लिंक पर क्लिक करें: {{url}}",
|
||||
"invitePhone": "इसके बजाय फोन से जुड़ने के लिए, इस पर टैप करें:: {{number}},,{{conferenceID}}#\n",
|
||||
"invitePhoneAlternatives": "Looking for a different dial-in number?\nSee meeting dial-in numbers: {{url}}\n\n\nIf also dialing-in through a room phone, join without connecting to audio: {{silentUrl}}",
|
||||
"inviteURLFirstPartGeneral": "आपको एक बैठक में शामिल होने के लिए आमंत्रित किया गया है।",
|
||||
"inviteURLFirstPartPersonal": "{{name}} आपको मीटिंग के लिए आमंत्रित कर रहा है।\n",
|
||||
"inviteURLSecondPart": "\nबैठक में शामिल हों:\n{{url}}\n",
|
||||
"liveStreamURL": "लाइव स्ट्रीम:",
|
||||
"moreNumbers": "अधिक संख्या",
|
||||
"noNumbers": "कोई डायल-इन नंबर नहीं।",
|
||||
"noPassword": "कोई नहीं",
|
||||
"noRoom": "No room was specified to dial-in into.",
|
||||
"numbers": "डायल-इन नंबर",
|
||||
"password": "$t(lockRoomPasswordUppercase):",
|
||||
"title": "साझा करें",
|
||||
"tooltip": "इस मीटिंग के लिए लिंक और डायल-इन जानकारी साझा करें",
|
||||
"label": "डायल-इन जानकारी"
|
||||
},
|
||||
"inviteDialog": {
|
||||
"alertText": "कुछ प्रतिभागियों को आमंत्रित करने में विफल।",
|
||||
"header": "आमंत्रित करें",
|
||||
"searchCallOnlyPlaceholder": "फ़ोन नंबर दर्ज करें",
|
||||
"searchPeopleOnlyPlaceholder": "प्रतिभागियों को खोजें",
|
||||
"searchPlaceholder": "प्रतिभागी या फ़ोन नंबर",
|
||||
"send": "भेजें"
|
||||
},
|
||||
"inlineDialogFailure": {
|
||||
"msg": "We stumbled a bit.",
|
||||
"retry": "पुनः प्रयास करें",
|
||||
"support": "सहायता",
|
||||
"supportMsg": "ऐसा बार बार हो रहा हो, तो सम्पर्क करे "
|
||||
},
|
||||
"keyboardShortcuts": {
|
||||
"focusLocal": "अपने वीडियो पर केंद्रित करें",
|
||||
"focusRemote": "किसी अन्य व्यक्ति के वीडियो पर केंद्रित करें",
|
||||
"fullScreen": "View or exit full screen",
|
||||
"keyboardShortcuts": "कीबोर्ड शॉर्टकट्स",
|
||||
"localRecording": "स्थानीय रिकॉर्डिंग नियंत्रण दिखाएं या छिपाएँ",
|
||||
"mute": "अपने माइक्रोफ़ोन को म्यूट या अनम्यूट करें",
|
||||
"pushToTalk": "Push to talk",
|
||||
"raiseHand": "अपना हाथ उठाएँ या नीचे करें",
|
||||
"showSpeakerStats": "स्पीकर आंकड़े दिखाएं",
|
||||
"toggleChat": "चैट खोलें या बंद करें",
|
||||
"toggleFilmstrip": "वीडियो थंबनेल दिखाएं या छिपाएँ",
|
||||
"toggleScreensharing": "कैमरा और स्क्रीन शेयरिंग के बीच स्विच करें",
|
||||
"toggleShortcuts": "कीबोर्ड शॉर्टकट दिखाएं या छिपाएं",
|
||||
"videoMute": "अपना कैमरा प्रारंभ या बंद करें"
|
||||
},
|
||||
"liveStreaming": {
|
||||
"limitNotificationDescriptionWeb": "Due to high demand your streaming will be limited to {{limit}} min. For unlimited streaming try <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
|
||||
"limitNotificationDescriptionNative": "Your streaming will be limited to {{limit}} min. For unlimited streaming try {{app}}.",
|
||||
"busy": "We're working on freeing streaming resources. Please try again in a few minutes.",
|
||||
"busyTitle": "All streamers are currently busy",
|
||||
"changeSignIn": "Switch accounts.",
|
||||
"choose": "Choose a live stream",
|
||||
"chooseCTA": "Choose a streaming option. You're currently logged in as {{email}}.",
|
||||
"enterStreamKey": "Enter your YouTube live stream key here.",
|
||||
"error": "Live Streaming failed. Please try again.",
|
||||
"errorAPI": "An error occurred while accessing your YouTube broadcasts. Please try logging in again.",
|
||||
"errorLiveStreamNotEnabled": "Live Streaming is not enabled on {{email}}. Please enable live streaming or log into an account with live streaming enabled.",
|
||||
"expandedOff": "The live streaming has stopped",
|
||||
"expandedOn": "The meeting is currently being streamed to YouTube.",
|
||||
"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.",
|
||||
"invalidStreamKey": "Live stream key may be incorrect.",
|
||||
"off": "Live Streaming stopped",
|
||||
"offBy": "{{name}} stopped the live streaming",
|
||||
"on": "Live Streaming started",
|
||||
"onBy": "{{name}} started the live streaming",
|
||||
"pending": "Starting Live Stream...",
|
||||
"serviceName": "Live Streaming service",
|
||||
"signedInAs": "You are currently signed in as:",
|
||||
"signIn": "Sign in with Google",
|
||||
"signInCTA": "Sign in or enter your live stream key from YouTube.",
|
||||
"signOut": "Sign out",
|
||||
"start": "Start a live stream",
|
||||
"streamIdHelp": "What's this?",
|
||||
"unavailableTitle": "Live Streaming unavailable",
|
||||
"youtubeTerms": "YouTube terms of services",
|
||||
"googlePrivacyPolicy": "Google Privacy Policy"
|
||||
},
|
||||
"localRecording": {
|
||||
"clientState": {
|
||||
"off": "Off",
|
||||
"on": "On",
|
||||
"unknown": "Unknown"
|
||||
},
|
||||
"dialogTitle": "Local Recording Controls",
|
||||
"duration": "Duration",
|
||||
"durationNA": "N/A",
|
||||
"encoding": "Encoding",
|
||||
"label": "LOR",
|
||||
"labelToolTip": "Local recording is engaged",
|
||||
"localRecording": "Local Recording",
|
||||
"me": "Me",
|
||||
"messages": {
|
||||
"engaged": "Local recording engaged.",
|
||||
"finished": "Recording session {{token}} finished. Please send the recorded file to the moderator.",
|
||||
"finishedModerator": "Recording session {{token}} finished. The recording of the local track has been saved. Please ask the other participants to submit their recordings.",
|
||||
"notModerator": "You are not the moderator. You cannot start or stop local recording."
|
||||
},
|
||||
"moderator": "Moderator",
|
||||
"no": "No",
|
||||
"participant": "Participant",
|
||||
"participantStats": "Participant Stats",
|
||||
"sessionToken": "Session Token",
|
||||
"start": "Start Recording",
|
||||
"stop": "Stop Recording",
|
||||
"yes": "Yes"
|
||||
},
|
||||
"lockRoomPassword": "पासवर्ड",
|
||||
"lockRoomPasswordUppercase": "पासवर्ड",
|
||||
"me": "मैं",
|
||||
"notify": {
|
||||
"connectedOneMember": "{{name}} मीटिंग में शामिल हुए",
|
||||
"connectedThreePlusMembers": "{{name}} और {{count}} अन्य लोग मीटिंग में शामिल हुए",
|
||||
"connectedTwoMembers": "{{first}} और {{second}} मीटिंग में शामिल हुआ",
|
||||
"disconnected": "डिस्कनेक्ट",
|
||||
"focus": "Conference focus",
|
||||
"focusFail": "{{component}} उपलब्ध नहीं - {{ms}} सेकंड में पुनः प्रयास करें",
|
||||
"grantedTo": "Moderator rights granted to {{to}}!",
|
||||
"invitedOneMember": "{{name}} को आमंत्रित किया गया",
|
||||
"invitedThreePlusMembers": "{{name}} और {{count}} अन्य लोगों को आमंत्रित किया गया",
|
||||
"invitedTwoMembers": "{{first}} और {{second}} को आमंत्रित किया गया",
|
||||
"kickParticipant": "{{kicked}} को {{kicker}} द्वारा किक किया गया",
|
||||
"me": "मैं",
|
||||
"moderator": "मॉडरेटर के अधिकार दिए गए!",
|
||||
"muted": "You have started the conversation muted.",
|
||||
"mutedTitle": "आप मौन हैं!",
|
||||
"mutedRemotelyTitle": "आपको {{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": "आपका कैमरा {{participantDisplayName}}द्वारा अक्षम कर दिया गया है!",
|
||||
"videoMutedRemotelyDescription": "You can always turn it on again.",
|
||||
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) किसी अन्य प्रतिभागी द्वारा हटा दिया गया",
|
||||
"passwordSetRemotely": "$t(lockRoomPasswordUppercase) दूसरे प्रतिभागी द्वारा निर्धारित",
|
||||
"raisedHand": "{{name}} बोलना चाहेंगे।",
|
||||
"somebody": "Somebody",
|
||||
"startSilentTitle": "आप बिना ऑडियो आउटपुट के साथ शामिल हुए!",
|
||||
"startSilentDescription": "ऑडियो सक्षम करने के लिए मीटिंग को फिर से करें",
|
||||
"suboptimalBrowserWarning": "We are afraid your meeting experience isn't going to be that great here. We are looking for ways to improve this, but until then please try using one of the <a href='{{recommendedBrowserPageLink}}' target='_blank'>fully supported browsers</a>.",
|
||||
"suboptimalExperienceTitle": "ब्राउज़र चेतावनी",
|
||||
"unmute": "अनम्यूट",
|
||||
"newDeviceCameraTitle": "नए कैमरे का पता चला",
|
||||
"newDeviceAudioTitle": "नए ऑडियो डिवाइस का पता चला",
|
||||
"newDeviceAction": "उपयोग करें",
|
||||
"OldElectronAPPTitle": "Security vulnerability!",
|
||||
"oldElectronClientDescription1": "आप जित्सी मीट क्लाइंट के एक पुराने संस्करण का उपयोग करते हुए दिखाई देते हैं, जिसमे सुरक्षा कमजोरियां ज्ञात हैं।",
|
||||
"oldElectronClientDescription2": "नवीनतम बिल्ड",
|
||||
"oldElectronClientDescription3": " अब!"
|
||||
},
|
||||
"passwordSetRemotely": "दूसरे प्रतिभागी द्वारा निर्धारित",
|
||||
"passwordDigitsOnly": "Up to {{number}} digits",
|
||||
"poweredby": "powered by",
|
||||
"prejoin": {
|
||||
"audioAndVideoError": "ऑडियो और वीडियो त्रुटि:",
|
||||
"audioDeviceProblem": "आपके ऑडियो डिवाइस में कोई समस्या है",
|
||||
"audioOnlyError": "ऑडियो त्रुटि:",
|
||||
"audioTrackError": "ऑडियो ट्रैक नहीं बना सका",
|
||||
"calling": "कॉलिंग",
|
||||
"callMe": "मुझे कॉल करें",
|
||||
"callMeAtNumber": "मुझे इस नंबर पर कॉल करें:",
|
||||
"configuringDevices": "डिवाइस कॉन्फ़िगर कर रहा है ...",
|
||||
"connectedWithAudioQ": "You’re connected with audio?",
|
||||
"connection": {
|
||||
"good": "Your internet connection looks good!",
|
||||
"nonOptimal": "Your internet connection is not optimal",
|
||||
"poor": "आपके पास एक खराब इंटरनेट कनेक्शन है"
|
||||
},
|
||||
"connectionDetails": {
|
||||
"audioClipping": "We expect your audio to be clipped.",
|
||||
"audioHighQuality": "We expect your audio to have excellent quality.",
|
||||
"audioLowNoVideo": "We expect your audio quality to be low and no video.",
|
||||
"goodQuality": "Awesome! Your media quality is going to be great.",
|
||||
"noMediaConnectivity": "We could not find a way to establish media connectivity for this test. This is typically caused by a firewall or NAT.",
|
||||
"noVideo": "We expect that your video will be terrible.",
|
||||
"undetectable": "If you still can not make calls in browser, we recommend that you make sure your speakers, microphone and camera are properly set up, that you have granted your browser rights to use your microphone and camera, and that your browser version is up-to-date. If you still have trouble calling, you should contact the web application developer.",
|
||||
"veryPoorConnection": "We expect your call quality to be really terrible.",
|
||||
"videoFreezing": "We expect your video to freeze, turn black, and be pixelated.",
|
||||
"videoHighQuality": "We expect your video to have good quality.",
|
||||
"videoLowQuality": "We expect your video to have low quality in terms of frame rate and resolution.",
|
||||
"videoTearing": "We expect your video to be pixelated or have visual artefacts."
|
||||
},
|
||||
"copyAndShare": "मीटिंग लिंक कॉपी और साझा करे ",
|
||||
"dialInMeeting": "मीटिंग में डायल करें",
|
||||
"dialInPin": "मीटिंग में डायल करें और पिन कोड डालें:",
|
||||
"dialing": "डायलिंग",
|
||||
"doNotShow": "इस स्क्रीन को फिर से न दिखाएं",
|
||||
"errorDialOut": "डायल नहीं कर सका",
|
||||
"errorDialOutDisconnected": "डायल नहीं किया जा सका। डिस्कनेक्ट किया गया",
|
||||
"errorDialOutFailed": "डायल नहीं कर सका। कॉल विफल",
|
||||
"errorDialOutStatus": "डायल आउट स्थिति प्राप्त करने में त्रुटि",
|
||||
"errorMissingName": "कृपया बैठक में शामिल होने के लिए अपना नाम दर्ज करें",
|
||||
"errorStatusCode": "त्रुटि डायलिंग आउट, स्थिति कोड: {{status}}",
|
||||
"errorValidation": "संख्या सत्यापन विफल",
|
||||
"iWantToDialIn": "मैं डायल करना चाहता हूं",
|
||||
"joinAudioByPhone": "फोन ऑडियो के साथ जुड़ें",
|
||||
"joinMeeting": "मीटिंग में शामिल हों",
|
||||
"joinWithoutAudio": "ऑडियो के बिना जुड़ें",
|
||||
"initiated": "कॉल आरंभ",
|
||||
"linkCopied": "लिंक क्लिपबोर्ड पर कॉपी किया गया",
|
||||
"lookGood": "ऐसा लगता है कि आपका माइक्रोफ़ोन ठीक से काम कर रहा है",
|
||||
"or": "या",
|
||||
"premeeting": "प्री मीटिंग",
|
||||
"showScreen": "प्री मीटिंग स्क्रीन सक्षम करें",
|
||||
"startWithPhone": "फोन ऑडियो से शुरू करें",
|
||||
"screenSharingError": "स्क्रीन शेयरिंग त्रुटि:",
|
||||
"videoOnlyError": "वीडियो त्रुटि:",
|
||||
"videoTrackError": "वीडियो ट्रैक नहीं बना सका",
|
||||
"viewAllNumbers": "सभी नंबर देखें"
|
||||
},
|
||||
"presenceStatus": {
|
||||
"busy": "व्यस्त",
|
||||
"calling": "कॉलिंग...",
|
||||
"connected": "कनेक्टेड",
|
||||
"connecting": "कनेक्टिंग...",
|
||||
"connecting2": "कनेक्टिंग*...",
|
||||
"disconnected": "डिस्कनेक्ट किया गया",
|
||||
"expired": "एक्सपायर्ड",
|
||||
"ignored": "Ignored",
|
||||
"initializingCall": "Initializing Call...",
|
||||
"invited": "आमंत्रित",
|
||||
"rejected": "अस्वीकृत",
|
||||
"ringing": "Ringing..."
|
||||
},
|
||||
"profile": {
|
||||
"setDisplayNameLabel": "अपना नाम सेट करें",
|
||||
"setEmailInput": "ई-मेल दर्ज करें",
|
||||
"setEmailLabel": "Set अपना ग्रेवार्ट ईमेल सेट करें",
|
||||
"title": "प्रोफ़ाइल"
|
||||
},
|
||||
"raisedHand": "बोलना चाहेंगे",
|
||||
"recording": {
|
||||
"limitNotificationDescriptionWeb": "Due to high demand your recording will be limited to {{limit}} min. For unlimited recordings try <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
|
||||
"limitNotificationDescriptionNative": "Due to high demand your recording will be limited to {{limit}} min. For unlimited recordings try <3>{{app}}</3>.",
|
||||
"authDropboxText": "Upload to Dropbox",
|
||||
"availableSpace": "Available space: {{spaceLeft}} MB (approximately {{duration}} minutes of recording)",
|
||||
"beta": "BETA",
|
||||
"busy": "We're working on freeing recording resources. Please try again in a few minutes.",
|
||||
"busyTitle": "All recorders are currently busy",
|
||||
"error": "Recording failed. Please try again.",
|
||||
"expandedOff": "Recording has stopped",
|
||||
"expandedOn": "The meeting is currently being recorded.",
|
||||
"expandedPending": "Recording is being started...",
|
||||
"failedToStart": "Recording failed to start",
|
||||
"fileSharingdescription": "Share recording with meeting participants",
|
||||
"live": "LIVE",
|
||||
"loggedIn": "Logged in as {{userName}}",
|
||||
"off": "Recording stopped",
|
||||
"offBy": "{{name}} stopped the recording",
|
||||
"on": "Recording started",
|
||||
"onBy": "{{name}} started the recording",
|
||||
"pending": "Preparing to record the meeting...",
|
||||
"rec": "REC",
|
||||
"serviceDescription": "Your recording will be saved by the recording service",
|
||||
"serviceDescriptionCloud": "Cloud recording",
|
||||
"serviceName": "Recording service",
|
||||
"signIn": "Sign in",
|
||||
"signOut": "Sign out",
|
||||
"unavailable": "Oops! The {{serviceName}} is currently unavailable. We're working on resolving the issue. Please try again later.",
|
||||
"unavailableTitle": "Recording unavailable"
|
||||
},
|
||||
"sectionList": {
|
||||
"pullToRefresh": "Pull to refresh"
|
||||
},
|
||||
"security": {
|
||||
"about": "You can add a $t(lockRoomPassword) to your meeting. Participants will need to provide the $t(lockRoomPassword) before they are allowed to join the meeting.",
|
||||
"aboutReadOnly": "Moderator participants can add a $t(lockRoomPassword) to the meeting. Participants will need to provide the $t(lockRoomPassword) before they are allowed to join the meeting.",
|
||||
"insecureRoomNameWarning": "The room name is unsafe. Unwanted participants may join your conference. Consider securing your meeting using the security button.",
|
||||
"securityOptions": "Security options"
|
||||
},
|
||||
"settings": {
|
||||
"calendar": {
|
||||
"about": "The {{appName}} calendar integration is used to securely access your calendar so it can read upcoming events.",
|
||||
"disconnect": "Disconnect",
|
||||
"microsoftSignIn": "Sign in with Microsoft",
|
||||
"signedIn": "Currently accessing calendar events for {{email}}. Click the Disconnect button below to stop accessing calendar events.",
|
||||
"title": "Calendar"
|
||||
},
|
||||
"devices": "डिवाइस",
|
||||
"followMe": "Everyone follows me",
|
||||
"language": "भाषा",
|
||||
"loggedIn": "Logged in as {{name}}",
|
||||
"microphones": "माइक्रोफोन",
|
||||
"moderator": "Moderator",
|
||||
"more": "More",
|
||||
"name": "नाम",
|
||||
"noDevice": "कोई नहीं",
|
||||
"selectAudioOutput": "ऑडियो आउटपुट",
|
||||
"selectCamera": "कैमरा",
|
||||
"selectMic": "माइक्रोफोन",
|
||||
"speakers": "Speakers",
|
||||
"startAudioMuted": "Everyone starts muted",
|
||||
"startVideoMuted": "Everyone starts hidden",
|
||||
"title": "सेटिंग"
|
||||
},
|
||||
"settingsView": {
|
||||
"advanced": "Advanced",
|
||||
"alertOk": "ओके",
|
||||
"alertCancel": "रद्द करें",
|
||||
"alertTitle": "चेतावनी",
|
||||
"alertURLText": "दर्ज किया गया सर्वर URL अमान्य है",
|
||||
"buildInfoSection": "Build Information",
|
||||
"conferenceSection": "सम्मेलन",
|
||||
"disableCallIntegration": "Disable native call integration",
|
||||
"disableP2P": "पीयर-टू-पीयर मोड को अक्षम करें",
|
||||
"disableCrashReporting": "क्रैश रिपोर्टिंग अक्षम करें",
|
||||
"disableCrashReportingWarning": "क्या आप वाकई क्रैश रिपोर्टिंग को अक्षम करना चाहते हैं? एप्लिकेशन को पुनरारंभ करने के बाद सेटिंग लागू की जाएगी",
|
||||
"displayName": "नाम",
|
||||
"email": "ईमेल",
|
||||
"header": "सेटिंग",
|
||||
"profileSection": "प्रोफाइल",
|
||||
"serverURL": "सर्वर URL",
|
||||
"showAdvanced": "Show advanced settings",
|
||||
"startWithAudioMuted": "Start with audio muted",
|
||||
"startWithVideoMuted": "Start with video muted",
|
||||
"version": "संस्करण"
|
||||
},
|
||||
"share": {
|
||||
"dialInfoText": "\n\n=====\n\nJust want to dial in on your phone?\n\n{{defaultDialInNumber}}Click this link to see the dial in phone numbers for this meeting\n{{dialInfoPageUrl}}",
|
||||
"mainText": "मीटिंग में शामिल होने के लिए निम्न लिंक पर क्लिक करें:\n{{roomUrl}}"
|
||||
},
|
||||
"speaker": "Speaker",
|
||||
"speakerStats": {
|
||||
"hours": "{{count}}h",
|
||||
"minutes": "{{count}}m",
|
||||
"name": "नाम",
|
||||
"seconds": "{{count}}s",
|
||||
"speakerStats": "Speaker Stats",
|
||||
"speakerTime": "Speaker Time"
|
||||
},
|
||||
"startupoverlay": {
|
||||
"policyText": " ",
|
||||
"genericTitle": "मीटिंग को आपके माइक्रोफ़ोन और कैमरे का उपयोग करने की आवश्यकता है।",
|
||||
"title": "{{app}} को आपके माइक्रोफ़ोन और कैमरे का उपयोग करने की आवश्यकता है।"
|
||||
},
|
||||
"suspendedoverlay": {
|
||||
"rejoinKeyTitle": "पुनः जुड़े",
|
||||
"text": "फिर से कनेक्ट करने के लिए <i>Rejoin</i> बटन दबाएं।",
|
||||
"title": "आपका वीडियो कॉल बाधित हो गया था क्योंकि यह कंप्यूटर स्लीप मोड में चला गया था "
|
||||
},
|
||||
"toolbar": {
|
||||
"accessibilityLabel": {
|
||||
"audioOnly": "केवल ऑडियो टॉगल करें",
|
||||
"audioRoute": "साउंड डिवाइस का चयन करें",
|
||||
"callQuality": "वीडियो गुणवत्ता प्रबंधित करें",
|
||||
"cc": "टॉगल उपशीर्षक",
|
||||
"chat": "चैट विंडो टॉगल करें",
|
||||
"document": "साझा दस्तावेज़ टॉगल करें",
|
||||
"download": "हमारे एप्लिकेशन डाउनलोड करें",
|
||||
"embedMeeting": "एंबेड मीटिंग",
|
||||
"feedback": "प्रतिक्रिया छोड़ें",
|
||||
"fullScreen": "फुल स्क्रीन को टॉगल करें",
|
||||
"grantModerator": "Grant Moderator",
|
||||
"hangup": "कॉल छोड़ें",
|
||||
"help": "सहायता",
|
||||
"invite": "लोगों को आमंत्रित करें",
|
||||
"kick": "Kick participant",
|
||||
"lobbyButton": "लॉबी मोड को सक्षम / अक्षम करें",
|
||||
"localRecording": "स्थानीय रिकॉर्डिंग नियंत्रणों को टॉगल करें",
|
||||
"lockRoom": "मीटिंग पासवर्ड टॉगल करें",
|
||||
"moreActions": "अधिक क्रिया मेनू को टॉगल करें",
|
||||
"moreActionsMenu": "अधिक क्रिया मेनू",
|
||||
"moreOptions": "अधिक विकल्प दिखाएं",
|
||||
"mute": "टॉगल म्यूट ऑडियो",
|
||||
"muteEveryone": "सभी को म्यूट करें",
|
||||
"muteEveryoneElse": "सभी को म्यूट करें",
|
||||
"muteEveryonesVideo": "सभी का कैमरा अक्षम करें",
|
||||
"muteEveryoneElsesVideo": "सभी का कैमरा अक्षम करें",
|
||||
"pip": "टॉगल पिक्चर-इन-पिक्चर मोड",
|
||||
"privateMessage": "निजी संदेश भेजें",
|
||||
"profile": "अपना प्रोफ़ाइल संपादित करें",
|
||||
"raiseHand": "Toggle raise hand",
|
||||
"recording": "टॉगल रिकॉर्डिंग",
|
||||
"remoteMute": "प्रतिभागी को म्यूट करें",
|
||||
"remoteVideoMute": "प्रतिभागी का कैमरा अक्षम करें",
|
||||
"security": "सुरक्षा विकल्प",
|
||||
"Settings": "सेटिंग टॉगल करें",
|
||||
"sharedvideo": "YouTube वीडियो साझाकरण टॉगल करें",
|
||||
"shareRoom": "किसी को आमंत्रित करें",
|
||||
"shareYourScreen": "टॉगल स्क्रीनशेयर",
|
||||
"shortcuts": "शॉर्टकट टॉगल करें",
|
||||
"show": "स्टेज पर दिखाएं",
|
||||
"speakerStats": "स्पीकर के आंकड़ों को टॉगल करें",
|
||||
"tileView": "टॉगल टाइल दृश्य",
|
||||
"toggleCamera": "कैमरा टॉगल करें",
|
||||
"toggleFilmstrip": "टॉगल फिल्मस्ट्रिप",
|
||||
"videomute": "टॉगल म्यूट वीडियो",
|
||||
"selectBackground": "पृष्ठभूमि का चयन करें"
|
||||
},
|
||||
"addPeople": "अपने कॉल में लोगों को जोड़ें",
|
||||
"audioSettings": "ऑडियो सेटिंग्स",
|
||||
"audioOnlyOff": "कम बैंडविड्थ मोड अक्षम करें",
|
||||
"audioOnlyOn": "कम बैंडविड्थ मोड सक्षम करें",
|
||||
"audioRoute": "साउंड डिवाइस का चयन करें",
|
||||
"authenticate": "Authenticate",
|
||||
"callQuality": "वीडियो गुणवत्ता प्रबंधित करें",
|
||||
"chat": "ओपन / क्लोज चैट",
|
||||
"closeChat": "क्लोज़ चैट",
|
||||
"documentClose": "साझा किए गए दस्तावेज़ को बंद करें",
|
||||
"documentOpen": "साझा दस्तावेज़ खोलें",
|
||||
"download": "हमारे एप्लिकेशन डाउनलोड करें",
|
||||
"e2ee": "एंड-टू-एंड एन्क्रिप्शन",
|
||||
"embedMeeting": "Embed meeting",
|
||||
"enterFullScreen": "View full screen",
|
||||
"enterTileView": "Enter tile view",
|
||||
"exitFullScreen": "Exit full screen",
|
||||
"exitTileView": "Exit tile view",
|
||||
"feedback": "प्रतिक्रिया छोड़ें",
|
||||
"hangup": "छोड़ें",
|
||||
"help": "Help",
|
||||
"invite": "लोगों को आमंत्रित करें",
|
||||
"lobbyButtonDisable": "लॉबी मोड को अक्षम करें",
|
||||
"lobbyButtonEnable": "लॉबी मोड सक्षम करें",
|
||||
"login": "लॉग इन",
|
||||
"logout": "लॉगआउट",
|
||||
"lowerYourHand": "Lower your hand",
|
||||
"moreActions": "More actions",
|
||||
"moreOptions": "अधिक विकल्प",
|
||||
"mute": "म्यूट / अनम्यूट",
|
||||
"muteEveryone": "सभी को म्यूट करें",
|
||||
"muteEveryonesVideo": "सभी का कैमरा अक्षम करें",
|
||||
"noAudioSignalTitle": "आपके माइक से कोई इनपुट नहीं आ रहा है!",
|
||||
"noAudioSignalDesc": "यदि आपने सिस्टम सेटिंग्स या हार्डवेयर से जानबूझकर इसे म्यूट नहीं किया है, तो डिवाइस को स्विच करने पर विचार करें",
|
||||
"noAudioSignalDescSuggestion": "यदि आपने सिस्टम सेटिंग्स या हार्डवेयर से जानबूझकर इसे म्यूट नहीं किया है, तो सुझाए गए डिवाइस पर स्विच करने पर विचार करें",
|
||||
"noAudioSignalDialInDesc": "आप डायल-इन का भी उपयोग कर सकते हैं:",
|
||||
"noAudioSignalDialInLinkDesc": "डायल-इन नंबर",
|
||||
"noisyAudioInputTitle": "आपका माइक्रोफ़ोन शोर कर रहा है!",
|
||||
"noisyAudioInputDesc": "ऐसा लगता है कि आपका माइक्रोफ़ोन शोर कर रहा है, कृपया डिवाइस को म्यूट करने या बदलने पर विचार करें",
|
||||
"openChat": "ओपन चैट",
|
||||
"pip": "पिक्चर-इन-पिक्चर मोड",
|
||||
"privateMessage": "निजी संदेश भेजें",
|
||||
"profile": "अपना प्रोफ़ाइल संपादित करें",
|
||||
"raiseHand": "अपना हाथ उठाएँ / नीचे करें",
|
||||
"raiseYourHand": "अपना हाथ उठाएं",
|
||||
"security": "सुरक्षा विकल्प",
|
||||
"Settings": "सेटिंग",
|
||||
"sharedvideo": "एक YouTube वीडियो साझा करें",
|
||||
"shareRoom": "किसी को आमंत्रित करें",
|
||||
"shortcuts": "शॉर्टकट देखें",
|
||||
"speakerStats": "स्पीकर आँकड़े",
|
||||
"startScreenSharing": "स्क्रीन साझाकरण प्रारंभ करें",
|
||||
"startSubtitles": "Start subtitles",
|
||||
"stopScreenSharing": "स्क्रीन शेयरिंग बंद करो",
|
||||
"stopSubtitles": "Stop subtitles",
|
||||
"stopSharedVideo": "YouTube वीडियो बंद करें",
|
||||
"talkWhileMutedPopup": "बोलने की कोशिश कर रहा है? आप मौन हैं",
|
||||
"tileViewToggle": "टॉगल टाइल दृश्य",
|
||||
"toggleCamera": "कैमरा टॉगल करें",
|
||||
"videomute": "स्टार्ट / स्टॉप कैमरा",
|
||||
"videoSettings": "वीडियो सेटिंग्स",
|
||||
"selectBackground": "पृष्ठभूमि का चयन करें"
|
||||
},
|
||||
"transcribing": {
|
||||
"ccButtonTooltip": "Start / Stop subtitles",
|
||||
"error": "ट्रांसक्रिप्शनिंग विफल रही। कृपया पुन: प्रयास करें",
|
||||
"expandedLabel": "वर्तमान में ट्रांसक्रिप्शनिंग चालू है",
|
||||
"failedToStart": "ट्रांसक्रिप्शनिंग प्रारंभ करने में विफल",
|
||||
"labelToolTip": "The meeting is being transcribed",
|
||||
"off": "ट्रांसक्रिप्शनिंग बंद कर दिया",
|
||||
"pending": "Preparing to transcribe the meeting...",
|
||||
"start": "उपशीर्षक दिखाना शुरू करें",
|
||||
"stop": "उपशीर्षक दिखाना बंद करें",
|
||||
"tr": "TR"
|
||||
},
|
||||
"userMedia": {
|
||||
"androidGrantPermissions": "Select <b><i>Allow</i></b> when your browser asks for permissions.",
|
||||
"chromeGrantPermissions": "Select <b><i>Allow</i></b> when your browser asks for permissions.",
|
||||
"edgeGrantPermissions": "Select <b><i>Yes</i></b> when your browser asks for permissions.",
|
||||
"electronGrantPermissions": "Trying to access your camera and microphone",
|
||||
"firefoxGrantPermissions": "Select <b><i>Share Selected Device</i></b> when your browser asks for permissions.",
|
||||
"iexplorerGrantPermissions": "Select <b><i>OK</i></b> when your browser asks for permissions.",
|
||||
"nwjsGrantPermissions": "Please grant permissions to use your camera and microphone",
|
||||
"operaGrantPermissions": "Select <b><i>Allow</i></b> when your browser asks for permissions.",
|
||||
"react-nativeGrantPermissions": "Select <b><i>Allow</i></b> when your browser asks for permissions.",
|
||||
"safariGrantPermissions": "Select <b><i>OK</i></b> when your browser asks for permissions."
|
||||
},
|
||||
"videoSIPGW": {
|
||||
"busy": "We're working on freeing resources. Please try again in a few minutes.",
|
||||
"busyTitle": "The Room service is currently busy",
|
||||
"errorAlreadyInvited": "{{displayName}} already invited",
|
||||
"errorInvite": "Conference not established yet. Please try again later.",
|
||||
"errorInviteFailed": "We're working on resolving the issue. Please try again later.",
|
||||
"errorInviteFailedTitle": "Inviting {{displayName}} failed",
|
||||
"errorInviteTitle": "Error inviting room",
|
||||
"pending": "{{displayName}} has been invited"
|
||||
},
|
||||
"videoStatus": {
|
||||
"audioOnly": "AUD",
|
||||
"audioOnlyExpanded": "You are in low bandwidth mode. In this mode you will receive only audio and screen sharing.",
|
||||
"callQuality": "Video Quality",
|
||||
"hd": "HD",
|
||||
"hdTooltip": "Viewing high definition video",
|
||||
"highDefinition": "High definition",
|
||||
"labelTooiltipNoVideo": "No video",
|
||||
"labelTooltipAudioOnly": "Low bandwidth mode enabled",
|
||||
"ld": "LD",
|
||||
"ldTooltip": "Viewing low definition video",
|
||||
"lowDefinition": "Low definition",
|
||||
"sd": "SD",
|
||||
"sdTooltip": "Viewing standard definition video",
|
||||
"standardDefinition": "Standard definition"
|
||||
},
|
||||
"videothumbnail": {
|
||||
"connectionInfo": "कनेक्शन जानकारी",
|
||||
"domute": "म्यूट",
|
||||
"domuteVideo": "कैमरा अक्षम करें",
|
||||
"domuteOthers": "सभी को म्यूट करें",
|
||||
"domuteVideoOfOthers": "Disable camera of everyone else",
|
||||
"flip": "Flip",
|
||||
"grantModerator": "Grant Moderator",
|
||||
"kick": "Kick out",
|
||||
"moderator": "Moderator",
|
||||
"mute": "प्रतिभागी मौन है",
|
||||
"muted": "म्यूटेड",
|
||||
"videoMuted": "कैमरा अक्षम",
|
||||
"remoteControl": "स्टार्ट / स्टॉप रिमोट कंट्रोल",
|
||||
"show": "स्टेज पर दिखाएं",
|
||||
"videomute": "प्रतिभागी ने कैमरा बंद कर दिया है"
|
||||
},
|
||||
"welcomepage": {
|
||||
"accessibilityLabel": {
|
||||
"join": "Tap to join",
|
||||
"roomname": "Enter room name"
|
||||
},
|
||||
"appDescription": "आगे बढ़ो, पूरी टीम के साथ वीडियो चैट करें। वास्तव में, हर किसी को जिसे आप जानते हैं, आमंत्रित करें। { {{app}} एक पूरी तरह से एन्क्रिप्टेड, 100% ओपन सोर्स वीडियो कॉन्फ्रेंसिंग समाधान है जिसका आप मुफ्त में - बिना किसी खाते की आवश्यकता के पूरे दिन, हर दिन, उपयोग कर सकते हैं।",
|
||||
"audioVideoSwitch": {
|
||||
"audio": "आवाज",
|
||||
"video": "वीडियो"
|
||||
},
|
||||
"calendar": "कैलेंडर",
|
||||
"connectCalendarButton": "अपने कैलेंडर को कनेक्ट करें",
|
||||
"connectCalendarText": "{{app}} में अपनी सभी मीटिंग्स देखने के लिए अपने कैलेंडर से कनेक्ट करें। इसके अलावा, अपने कैलेंडर में {{provider}} मीटिंग्स जोड़ें और उन्हें एक क्लिक के साथ प्रारंभ करें",
|
||||
"enterRoomTitle": "एक नई बैठक शुरू करें",
|
||||
"getHelp": "सहायता प्राप्त करें",
|
||||
"go": "GO",
|
||||
"goSmall": "GO",
|
||||
"headerTitle": "जित्सी मीट",
|
||||
"headerSubtitle": "सुरक्षित और उच्च गुणवत्ता बैठकें",
|
||||
"info": "डायल-इन जानकारी",
|
||||
"join": "बनाये / जुड़े ",
|
||||
"jitsiOnMobile": "मोबाइल पर Jitsi – हमारे एप्लिकेशन डाउनलोड करें और कहीं से भी एक बैठक शुरू करें",
|
||||
"moderatedMessage": "Or <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">book a meeting URL</a> in advance where you are the only moderator.",
|
||||
"privacy": "गोपनीयता",
|
||||
"recentList": "हाल का",
|
||||
"recentListDelete": "प्रविष्टि हटाएं",
|
||||
"recentListEmpty": "आपकी हाल की सूची वर्तमान में खाली है। अपनी टीम के साथ चैट करें और आपको अपनी सभी हालिया बैठकें मिलेंगी",
|
||||
"reducedUIText": "{{app}} में आपका स्वागत है!",
|
||||
"roomNameAllowedChars": " मीटिंग नाम में इनमें से कोई भी वर्ण नहीं होना चाहिए: ?, &, :, ', \", %, #.",
|
||||
"roomname": "कक्ष का नाम दर्ज करें",
|
||||
"roomnameHint": "जिस कक्ष में आप शामिल होना चाहते हैं उसका नाम या URL दर्ज करें। आप एक नाम बना सकते हैं, बस जिन लोगों से आप मिल रहे हैं, उन्हें यह बताएं ताकि वे उसी नाम को दर्ज करें",
|
||||
"sendFeedback": "फ़ीडबैक भेजें",
|
||||
"startMeeting": "मीटिंग प्रारंभ करें",
|
||||
"terms": "शर्तें",
|
||||
"title": "सुरक्षित, पूरी तरह से चित्रित, और पूरी तरह से मुक्त वीडियो कॉन्फ्रेंसिंग"
|
||||
},
|
||||
"lonelyMeetingExperience": {
|
||||
"button": "दूसरों को आमंत्रित करें",
|
||||
"youAreAlone": "मीटिंग में केवल आप ही हैं"
|
||||
},
|
||||
"helpView": {
|
||||
"header": "सहायता केंद्र"
|
||||
},
|
||||
"lobby": {
|
||||
"knockingParticipantList": "प्रतिभागी सूची दस्तक",
|
||||
"allow": "अनुमति दें",
|
||||
"backToKnockModeButton": "कोई पासवर्ड नहीं, इसके बजाय जुड़ने के लिए कहें",
|
||||
"dialogTitle": "लॉबी मोड",
|
||||
"disableDialogContent": "लॉबी मोड वर्तमान में सक्षम है। यह सुविधा सुनिश्चित करती है कि अवांछित प्रतिभागी आपकी मीटिंग में शामिल नहीं हो सकते। क्या आप इसे अक्षम करना चाहते हैं?",
|
||||
"disableDialogSubmit": "अक्षम करें",
|
||||
"emailField": "अपना ईमेल पता दर्ज करें",
|
||||
"enableDialogPasswordField": "पासवर्ड सेट करें (वैकल्पिक)",
|
||||
"enableDialogSubmit": "सक्षम करें",
|
||||
"enableDialogText": "Lobby mode lets you protect your meeting by only allowing people to enter after a formal approval by a moderator.",
|
||||
"enterPasswordButton": "मीटिंग पासवर्ड दर्ज करें",
|
||||
"enterPasswordTitle": "मीटिंग में शामिल होने के लिए पासवर्ड दर्ज करें",
|
||||
"invalidPassword": "अमान्य पासवर्ड",
|
||||
"joiningMessage": "जैसे ही कोई आपके अनुरोध को स्वीकार करता है आप बैठक में शामिल हो जाएंगे",
|
||||
"joinWithPasswordMessage": "पासवर्ड के साथ जुड़ने की कोशिश कर रहा है, कृपया प्रतीक्षा करें...",
|
||||
"joinRejectedMessage": "आपका अनुरोध एक मॉडरेटर द्वारा अस्वीकार कर दिया गया.",
|
||||
"joinTitle": "मीटिंग में शामिल हों",
|
||||
"joiningTitle": "मीटिंग में शामिल होने के लिए कह रहा है...",
|
||||
"joiningWithPasswordTitle": "पासवर्ड के साथ जुड़े...",
|
||||
"knockButton": "जुड़ने के लिए कहें ",
|
||||
"knockTitle": "कोई व्यक्ति बैठक में शामिल होना चाहता है",
|
||||
"nameField": "अपना नाम दर्ज करें",
|
||||
"notificationLobbyAccessDenied": "{{targetParticipantName}} has been rejected to join by {{originParticipantName}}",
|
||||
"notificationLobbyAccessGranted": "{{targetParticipantName}} has been allowed to join by {{originParticipantName}}",
|
||||
"notificationLobbyDisabled": "लॉबी को {{originParticipantName}}द्वारा अक्षम कर दिया गया",
|
||||
"notificationLobbyEnabled": "लॉबी को {{originParticipantName}}द्वारा सक्षम किया गया",
|
||||
"notificationTitle": "लॉबी",
|
||||
"passwordField": "मीटिंग पासवर्ड दर्ज करें",
|
||||
"passwordJoinButton": "Join",
|
||||
"reject": "अस्वीकार",
|
||||
"toggleLabel": "लॉबी सक्षम करें"
|
||||
}
|
||||
}
|
||||
@@ -204,6 +204,9 @@
|
||||
"done": "Gereed",
|
||||
"e2eeDescription": "Eind-tot-Eind-Versleuteling is momenteel EXPERIMENTEEL. Houd er rekening mee dat inschakelen van eind-tot-eind-versleuteling de door de server geleverde services zal uitschakelen zoals: opnemen, livestreamen en deelname via telefoon. Houd er ook rekening mee dat de vergadering alleen zal werken voor personen die deelnemen vanaf browsers met ondersteuning voor insertable streams.",
|
||||
"e2eeLabel": "Sleutel",
|
||||
"e2eeNoKey": "Geen",
|
||||
"e2eeToggleSet": "Sleutel instellen",
|
||||
"e2eeSet": "Instellen",
|
||||
"e2eeWarning": "WAARSCHUWING: Niet alle deelnemers in deze vergadering lijken ondersteuning te hebben voor eind-tot-eind-versleuteling. Als u het inschakelt zullen zij u niet kunnen zien of horen.",
|
||||
"enterDisplayName": "Voer hier uw naam in",
|
||||
"error": "Fout",
|
||||
@@ -265,6 +268,7 @@
|
||||
"readMore": "meer",
|
||||
"recording": "Opnemen",
|
||||
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "Niet mogelijk tijdens een livestream",
|
||||
"recordingDisabledForGuestTooltip": "Gasten kunnen geen opnamen starten.",
|
||||
"recordingDisabledTooltip": "Opname starten uitgeschakeld.",
|
||||
"rejoinNow": "Nu opnieuw deelnemen",
|
||||
"remoteControlAllowedMessage": "{{user}} heeft uw verzoek om extern beheer geaccepteerd.",
|
||||
@@ -297,6 +301,7 @@
|
||||
"shareVideoTitle": "Een video delen",
|
||||
"shareYourScreen": "Uw scherm delen",
|
||||
"shareYourScreenDisabled": "Schermdeling is uitgeschakeld.",
|
||||
"shareYourScreenDisabledForGuest": "Gasten kunnen hun scherm niet delen.",
|
||||
"startLiveStreaming": "Livestream starten",
|
||||
"startRecording": "Opname starten",
|
||||
"startRemoteControlErrorMessage": "Er is een fout opgetreden tijdens het starten van de sessie van extern beheer.",
|
||||
@@ -317,6 +322,7 @@
|
||||
"videoLink": "Video link",
|
||||
"WaitForHostMsg": "De vergadering <b>{{room}}</b> is nog niet gestart. Authenticeer uzelf als u de host bent. Anders wacht u tot de host aanwezig is.",
|
||||
"WaitForHostMsgWOk": "De vergadering <b>{{room}}</b> is nog niet gestart. Als u de host bent, drukt u op 'OK' om uzelf te authenticeren. Anders wacht u tot de host aanwezig is.",
|
||||
"WaitingForHost": "Wachten op de host...",
|
||||
"Yes": "Ja",
|
||||
"yourEntireScreen": "Uw gehele scherm"
|
||||
},
|
||||
@@ -414,7 +420,8 @@
|
||||
"toggleFilmstrip": "Videominiaturen weergeven of verbergen",
|
||||
"toggleScreensharing": "Wisselen tussen camera en schermdeling",
|
||||
"toggleShortcuts": "Sneltoetsen weergeven of verbergen",
|
||||
"videoMute": "Uw camera starten of stoppen"
|
||||
"videoMute": "Uw camera starten of stoppen",
|
||||
"videoQuality": "Kwaliteit van gesprek beheren"
|
||||
},
|
||||
"liveStreaming": {
|
||||
"limitNotificationDescriptionWeb": "Vanwege een grote vraag zal uw stream beperkt worden tot {{limit}} min. Voor ongelimiteerd streamen, probeer <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
|
||||
@@ -694,7 +701,7 @@
|
||||
"chat": "Chatvenster in- of uitschakelen",
|
||||
"document": "Gedeeld document in- of uitschakelen",
|
||||
"download": "Download onze apps",
|
||||
"embedMeeting": "Vergadering inbedden",
|
||||
"e2ee": "Eind-tot-eind-versleuteling",
|
||||
"feedback": "Feedback achterlaten",
|
||||
"fullScreen": "Volledig scherm in- of uitschakelen",
|
||||
"grantModerator": "Moderatorrechten verlenen",
|
||||
@@ -728,10 +735,9 @@
|
||||
"toggleCamera": "Camera wisselen",
|
||||
"toggleFilmstrip": "Filmstrip in- of uitschakelen",
|
||||
"videomute": "Video dempen in- of uitschakelen",
|
||||
"selectBackground": "Achtergrond selecteren"
|
||||
"videoblur": "Video vervagen in- of uitschakelen"
|
||||
},
|
||||
"addPeople": "Personen aan uw gesprek toevoegen",
|
||||
"audioSettings": "Audio-instellingen",
|
||||
"audioOnlyOff": "Lage bandbreedte-modus uitschakelen",
|
||||
"audioOnlyOn": "Lage bandbreedte-modus inschakelen",
|
||||
"audioRoute": "Het afspeelapparaat selecteren",
|
||||
@@ -743,7 +749,6 @@
|
||||
"documentOpen": "Gedeeld document openen",
|
||||
"download": "Download onze apps",
|
||||
"e2ee": "Eind-tot-eind-versleuteling",
|
||||
"embedMeeting": "Vergadering inbedden",
|
||||
"enterFullScreen": "Volledig scherm weergeven",
|
||||
"enterTileView": "Tegelweergave openen",
|
||||
"exitFullScreen": "Volledig scherm sluiten",
|
||||
@@ -761,7 +766,6 @@
|
||||
"moreOptions": "Meer opties",
|
||||
"mute": "Dempen / Dempen opheffen",
|
||||
"muteEveryone": "Iedereen dempen",
|
||||
"muteEveryonesVideo": "Camera's van iedereen uitzetten",
|
||||
"noAudioSignalTitle": "Er komt geen invoer van uw microfoon!",
|
||||
"noAudioSignalDesc": "Als u niet met opzet hebt gedempt vanuit systeeminstellingen of hardware, overweeg dan van apparaat te wisselen.",
|
||||
"noAudioSignalDescSuggestion": "Als u niet met opzet hebt gedempt vanuit systeeminstellingen of hardware, overweeg dan over te schakelen naar het gesuggereerde apparaat.",
|
||||
@@ -790,8 +794,7 @@
|
||||
"tileViewToggle": "Tegelweergave in- of uitschakelen",
|
||||
"toggleCamera": "Camera in- of uitschakelen",
|
||||
"videomute": "Camera starten / stoppen",
|
||||
"videoSettings": "Instellingen van camera",
|
||||
"selectBackground": "Achtergrond selecteren",
|
||||
"startvideoblur": "Vervaag mijn achtergrond",
|
||||
"stopvideoblur": "Achtergrond vervagen uitschakelen"
|
||||
},
|
||||
"transcribing": {
|
||||
@@ -840,6 +843,8 @@
|
||||
"ld": "LD",
|
||||
"ldTooltip": "U bekijkt video in lage resolutie",
|
||||
"lowDefinition": "Lage resolutie",
|
||||
"onlyAudioAvailable": "Alleen audio is beschikbaar",
|
||||
"onlyAudioSupported": "In deze browser wordt alleen audio ondersteund.",
|
||||
"sd": "SD",
|
||||
"sdTooltip": "U bekijkt video in standaard resolutie",
|
||||
"standardDefinition": "Standaard resolutie"
|
||||
@@ -874,8 +879,6 @@
|
||||
"getHelp": "Hulp krijgen",
|
||||
"go": "GAAN",
|
||||
"goSmall": "GAAN",
|
||||
"headerTitle": "Jitsi Meet",
|
||||
"headerSubtitle": "Veilige vergaderingen van hoge kwaliteit",
|
||||
"info": "Informatie",
|
||||
"join": "AANMAKEN / DEELNEMEN",
|
||||
"moderatedMessage": "Of <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">boek een vergadering URL</a> van tevoren waar u de enige moderator bent.",
|
||||
@@ -888,7 +891,6 @@
|
||||
"roomname": "Voer naam van ruimte in",
|
||||
"roomnameHint": "Voer de naam of URL in van de ruimte die u wilt betreden. U kunt een naam verzinnen, maar laat deze wel weten aan de andere deelnemers, zodat zij dezelfde naam invoeren.",
|
||||
"sendFeedback": "Feedback versturen",
|
||||
"startMeeting": "Vergadering starten",
|
||||
"terms": "Voorwaarden",
|
||||
"title": "Veilige, volledig uitgeruste en geheel gratis videovergaderingen"
|
||||
},
|
||||
|
||||
@@ -113,7 +113,6 @@
|
||||
"maxEnabledResolution": "send max",
|
||||
"more": "Zobraziť viac",
|
||||
"packetloss": "Strata paketov:",
|
||||
"participant_id": "ID účastníka:",
|
||||
"quality": {
|
||||
"good": "Dobré",
|
||||
"inactive": "Neaktívne",
|
||||
@@ -317,13 +316,10 @@
|
||||
"e2ee": {
|
||||
"labelToolTip": "Zvuková a obrazová komunikácia je koncovo šifrovaná"
|
||||
},
|
||||
"embedMeeting": {
|
||||
"title": "Vložiť toto stretnutie"
|
||||
},
|
||||
"feedback": {
|
||||
"average": "Priemerný",
|
||||
"bad": "Zlý",
|
||||
"detailsLabel": "Povedzte nám viac",
|
||||
"detailsLabel": "Povedzte nám viac.",
|
||||
"good": "Dobrý",
|
||||
"rateExperience": "Ohodnoťte dojem",
|
||||
"veryBad": "Veľmi zlý",
|
||||
@@ -766,8 +762,7 @@
|
||||
"toggleCamera": "Zmeniť kameru",
|
||||
"videomute": "Vypnúť / Zapnúť kameru",
|
||||
"startvideoblur": "Rozmazať pozadie",
|
||||
"stopvideoblur": "Ukončiť rozmazanie pozadia",
|
||||
"selectBackground": "Vybrať pozadie"
|
||||
"stopvideoblur": "Ukončiť rozmazanie pozadia"
|
||||
},
|
||||
"transcribing": {
|
||||
"ccButtonTooltip": "titulky vypnuť/zapnúť",
|
||||
@@ -832,9 +827,7 @@
|
||||
"muted": "Vypnutý mikrofón",
|
||||
"remoteControl": "Vzdialené ovládanie",
|
||||
"show": "Ukázať v popredí",
|
||||
"videomute": "Účastník vypol kameru",
|
||||
"videoMuted": "Vypnutá kamera",
|
||||
"domuteVideoOfOthers": "Vypnúť kamery ostatným"
|
||||
"videomute": "Účastník vypol kameru"
|
||||
},
|
||||
"welcomepage": {
|
||||
"accessibilityLabel": {
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
"shareInvite": "Share meeting invitation",
|
||||
"shareLink": "Share the meeting link to invite others",
|
||||
"shareStream": "Share the live streaming link",
|
||||
"sip": "SIP: {{address}}",
|
||||
"telephone": "Telephone: {{number}}",
|
||||
"title": "Invite people to this meeting",
|
||||
"yahooEmail": "Yahoo Email"
|
||||
@@ -226,7 +225,7 @@
|
||||
"liveStreamingDisabledBecauseOfActiveRecordingTooltip": "Not possible while recording is active",
|
||||
"liveStreamingDisabledTooltip": "Start live stream disabled.",
|
||||
"lockMessage": "Failed to lock the conference.",
|
||||
"lockRoom": "Add meeting $t(lockRoomPassword)",
|
||||
"lockRoom": "Add meeting $t(lockRoomPasswordUppercase)",
|
||||
"lockTitle": "Lock failed",
|
||||
"logoutQuestion": "Are you sure you want to logout and stop the conference?",
|
||||
"login": "Login",
|
||||
@@ -339,8 +338,7 @@
|
||||
},
|
||||
"virtualBackground": {
|
||||
"title": "Backgrounds",
|
||||
"blur": "Blur",
|
||||
"slightBlur": "Slight Blur",
|
||||
"enableBlur": "Enable blur",
|
||||
"removeBackground": "Remove background",
|
||||
"uploadImage": "Upload image",
|
||||
"pleaseWait": "Please wait...",
|
||||
@@ -511,8 +509,6 @@
|
||||
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) removed by another participant",
|
||||
"passwordSetRemotely": "$t(lockRoomPasswordUppercase) set by another participant",
|
||||
"raisedHand": "{{name}} would like to speak.",
|
||||
"screenShareNoAudio": " Share audio box was not checked in the window selection screen.",
|
||||
"screenShareNoAudioTitle": "Share audio was not chcked",
|
||||
"somebody": "Somebody",
|
||||
"startSilentTitle": "You joined with no audio output!",
|
||||
"startSilentDescription": "Rejoin the meeting to enable audio",
|
||||
@@ -527,7 +523,7 @@
|
||||
"oldElectronClientDescription2": "latest build",
|
||||
"oldElectronClientDescription3": " now!"
|
||||
},
|
||||
"passwordSetRemotely": "Set by another participant",
|
||||
"passwordSetRemotely": "set by another participant",
|
||||
"passwordDigitsOnly": "Up to {{number}} digits",
|
||||
"poweredby": "powered by",
|
||||
"prejoin": {
|
||||
@@ -754,7 +750,6 @@
|
||||
"remoteVideoMute": "Disable camera of participant",
|
||||
"security": "Security options",
|
||||
"Settings": "Toggle settings",
|
||||
"shareaudio": "Share audio",
|
||||
"sharedvideo": "Toggle YouTube video sharing",
|
||||
"shareRoom": "Invite someone",
|
||||
"shareYourScreen": "Toggle screenshare",
|
||||
@@ -814,7 +809,6 @@
|
||||
"raiseYourHand": "Raise your hand",
|
||||
"security": "Security options",
|
||||
"Settings": "Settings",
|
||||
"shareaudio": "Share audio",
|
||||
"sharedvideo": "Share a YouTube video",
|
||||
"shareRoom": "Invite someone",
|
||||
"shortcuts": "View shortcuts",
|
||||
|
||||
@@ -38,7 +38,7 @@ import {
|
||||
resizeLargeVideo,
|
||||
selectParticipantInLargeVideo
|
||||
} from '../../react/features/large-video/actions';
|
||||
import { toggleLobbyMode } from '../../react/features/lobby/actions';
|
||||
import { toggleLobbyMode } from '../../react/features/lobby/actions.web';
|
||||
import { RECORDING_TYPES } from '../../react/features/recording/constants';
|
||||
import { getActiveSession } from '../../react/features/recording/functions';
|
||||
import { toggleTileView, setTileView } from '../../react/features/video-layout';
|
||||
|
||||
@@ -8,10 +8,3 @@
|
||||
* @type {{FEEDBACK_REQUEST_IN_PROGRESS: string}}
|
||||
*/
|
||||
export const FEEDBACK_REQUEST_IN_PROGRESS = 'FeedbackRequestInProgress';
|
||||
|
||||
/**
|
||||
* Indicated an attempted audio only screen share session with no audio track present
|
||||
*
|
||||
* @type {{AUDIO_ONLY_SCREEN_SHARE_NO_TRACK: string}}
|
||||
*/
|
||||
export const AUDIO_ONLY_SCREEN_SHARE_NO_TRACK = 'AudioOnlyScreenShareNoTrack';
|
||||
|
||||
8
package-lock.json
generated
8
package-lock.json
generated
@@ -10513,8 +10513,8 @@
|
||||
}
|
||||
},
|
||||
"lib-jitsi-meet": {
|
||||
"version": "github:jitsi/lib-jitsi-meet#49c4e75f37cfec5a5bdd921c869b6ec1bff8f9d2",
|
||||
"from": "github:jitsi/lib-jitsi-meet#49c4e75f37cfec5a5bdd921c869b6ec1bff8f9d2",
|
||||
"version": "github:jitsi/lib-jitsi-meet#a607af0b9e5f31566258786021575de62318965f",
|
||||
"from": "github:jitsi/lib-jitsi-meet#a607af0b9e5f31566258786021575de62318965f",
|
||||
"requires": {
|
||||
"@jitsi/js-utils": "1.0.2",
|
||||
"@jitsi/sdp-interop": "1.0.3",
|
||||
@@ -14457,8 +14457,8 @@
|
||||
"integrity": "sha512-iqdJ1KpZbR4XGahgVmaeibB7kDhyMT7wrylINgJaYBY38IAiI0LF32VX1umO4pko6n21YF5I/kSeNQ+OXGqqow=="
|
||||
},
|
||||
"react-native-webrtc": {
|
||||
"version": "github:react-native-webrtc/react-native-webrtc#05b7a06e04ad24fe6fa4e6b8118fbd21315152de",
|
||||
"from": "github:react-native-webrtc/react-native-webrtc#05b7a06e04ad24fe6fa4e6b8118fbd21315152de",
|
||||
"version": "github:react-native-webrtc/react-native-webrtc#1343580c0322f265ff0fb22722ac5501c5fd77ad",
|
||||
"from": "github:react-native-webrtc/react-native-webrtc#1343580c0322f265ff0fb22722ac5501c5fd77ad",
|
||||
"requires": {
|
||||
"base64-js": "^1.1.2",
|
||||
"cross-os": "^1.3.0",
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"jquery-i18next": "1.2.1",
|
||||
"js-md5": "0.6.1",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#49c4e75f37cfec5a5bdd921c869b6ec1bff8f9d2",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#a607af0b9e5f31566258786021575de62318965f",
|
||||
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.1",
|
||||
@@ -83,7 +83,7 @@
|
||||
"react-native-svg-transformer": "0.14.3",
|
||||
"react-native-url-polyfill": "1.2.0",
|
||||
"react-native-watch-connectivity": "0.4.3",
|
||||
"react-native-webrtc": "github:react-native-webrtc/react-native-webrtc#05b7a06e04ad24fe6fa4e6b8118fbd21315152de",
|
||||
"react-native-webrtc": "github:react-native-webrtc/react-native-webrtc#1343580c0322f265ff0fb22722ac5501c5fd77ad",
|
||||
"react-native-webview": "11.0.2",
|
||||
"react-native-youtube-iframe": "1.2.3",
|
||||
"react-redux": "7.1.0",
|
||||
|
||||
@@ -44,7 +44,6 @@ import '../recent-list/reducer';
|
||||
import '../recording/reducer';
|
||||
import '../settings/reducer';
|
||||
import '../subtitles/reducer';
|
||||
import '../screen-share/reducer';
|
||||
import '../toolbox/reducer';
|
||||
import '../transcribing/reducer';
|
||||
import '../video-layout/reducer';
|
||||
|
||||
@@ -32,7 +32,7 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
||||
}
|
||||
case TOGGLE_SCREENSHARING: {
|
||||
if (typeof APP === 'object') {
|
||||
APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING, action.audioOnly);
|
||||
APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING);
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -16,7 +16,7 @@ export const _CONFIG_STORE_PREFIX = 'config.js';
|
||||
export const TOOLBAR_BUTTONS = [
|
||||
'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',
|
||||
'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
|
||||
'livestreaming', 'etherpad', 'sharedvideo', 'shareaudio', 'settings', 'raisehand',
|
||||
'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',
|
||||
'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
|
||||
'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone',
|
||||
'security', 'toggle-camera'
|
||||
|
||||
@@ -116,24 +116,6 @@ const brandedDialogText = {
|
||||
textAlign: 'center'
|
||||
};
|
||||
|
||||
const brandedDialogLabelStyle = {
|
||||
color: schemeColor('text'),
|
||||
flexShrink: 1,
|
||||
fontSize: MD_FONT_SIZE,
|
||||
opacity: 0.90
|
||||
};
|
||||
|
||||
const brandedDialogItemContainerStyle = {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
height: MD_ITEM_HEIGHT
|
||||
};
|
||||
|
||||
const brandedDialogIconStyle = {
|
||||
color: schemeColor('icon'),
|
||||
fontSize: 24
|
||||
};
|
||||
|
||||
export const inputDialog = {
|
||||
bottomField: {
|
||||
marginBottom: 0
|
||||
@@ -163,22 +145,28 @@ ColorSchemeRegistry.register('BottomSheet', {
|
||||
* Style for the {@code Icon} element in a generic item of the menu.
|
||||
*/
|
||||
iconStyle: {
|
||||
...brandedDialogIconStyle
|
||||
color: schemeColor('icon'),
|
||||
fontSize: 24
|
||||
},
|
||||
|
||||
/**
|
||||
* Style for the label in a generic item rendered in the menu.
|
||||
*/
|
||||
labelStyle: {
|
||||
...brandedDialogLabelStyle,
|
||||
marginLeft: 32
|
||||
color: schemeColor('text'),
|
||||
flexShrink: 1,
|
||||
fontSize: MD_FONT_SIZE,
|
||||
marginLeft: 32,
|
||||
opacity: 0.90
|
||||
},
|
||||
|
||||
/**
|
||||
* Container style for a generic item rendered in the menu.
|
||||
*/
|
||||
style: {
|
||||
...brandedDialogItemContainerStyle
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
height: MD_ITEM_HEIGHT
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -270,28 +258,3 @@ ColorSchemeRegistry.register('Dialog', {
|
||||
borderTopWidth: 1
|
||||
}
|
||||
});
|
||||
|
||||
ColorSchemeRegistry.register('SecurityDialog', {
|
||||
/**
|
||||
* Field on an input dialog.
|
||||
*/
|
||||
field: {
|
||||
borderBottomWidth: 1,
|
||||
borderColor: schemeColor('border'),
|
||||
color: schemeColor('text'),
|
||||
fontSize: 14,
|
||||
paddingBottom: 8
|
||||
},
|
||||
|
||||
text: {
|
||||
color: schemeColor('text'),
|
||||
fontSize: 14,
|
||||
marginTop: 8
|
||||
},
|
||||
|
||||
title: {
|
||||
color: schemeColor('text'),
|
||||
fontSize: 18,
|
||||
fontWeight: 'bold'
|
||||
}
|
||||
});
|
||||
|
||||
@@ -41,15 +41,6 @@ export function isBrowsersOptimal(browserName: string) {
|
||||
.includes(browserName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the current OS is Mac.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isMacOS() {
|
||||
return Platform.OS === 'macos';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the current browser or the list of passed in browsers
|
||||
* is considered suboptimal. Suboptimal means it is a supported browser but has
|
||||
|
||||
@@ -86,12 +86,6 @@ export const INVITE_ENABLED = 'invite.enabled';
|
||||
*/
|
||||
export const IOS_RECORDING_ENABLED = 'ios.recording.enabled';
|
||||
|
||||
/**
|
||||
* Flag indicating if screen sharing should be enabled in iOS.
|
||||
* Default: disabled (false).
|
||||
*/
|
||||
export const IOS_SCREENSHARING_ENABLED = 'ios.screensharing.enabled';
|
||||
|
||||
/**
|
||||
* Flag indicating if kickout is enabled.
|
||||
* Default: enabled (true).
|
||||
|
||||
@@ -99,7 +99,6 @@ export { default as IconSignalLevel0 } from './signal_cellular_0.svg';
|
||||
export { default as IconSignalLevel1 } from './signal_cellular_1.svg';
|
||||
export { default as IconSignalLevel2 } from './signal_cellular_2.svg';
|
||||
export { default as IconShare } from './share.svg';
|
||||
export { default as IconShareAudio } from './share-audio.svg';
|
||||
export { default as IconShareDesktop } from './share-desktop.svg';
|
||||
export { default as IconShareDoc } from './share-doc.svg';
|
||||
export { default as IconShareVideo } from './shared-video.svg';
|
||||
@@ -112,7 +111,6 @@ export { default as IconVideoQualityAudioOnly } from './AUD.svg';
|
||||
export { default as IconVideoQualityHD } from './HD.svg';
|
||||
export { default as IconVideoQualityLD } from './LD.svg';
|
||||
export { default as IconVideoQualitySD } from './SD.svg';
|
||||
export { default as IconUserGroups } from './user-groups.svg';
|
||||
export { default as IconVirtualBackground } from './virtual-background.svg';
|
||||
export { default as IconVolume } from './volume.svg';
|
||||
export { default as IconVolumeEmpty } from './volume-empty.svg';
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.31658 3.06949L4.99999 6.66664H2.49999C2.03975 6.66664 1.66666 7.03974 1.66666 7.49998V12.5C1.66666 12.9602 2.03975 13.3333 2.49999 13.3333H4.99999L9.31658 16.9305C9.39146 16.9929 9.48585 17.027 9.58332 17.027C9.81344 17.027 9.99999 16.8405 9.99999 16.6104V3.38958C9.99999 3.2921 9.96582 3.19772 9.90341 3.12283C9.7561 2.94605 9.49336 2.92217 9.31658 3.06949ZM3.33332 8.33331H5.60341L8.33332 6.05838V13.9416L5.60341 11.6666H3.33332V8.33331ZM11.6667 6.66664C13.5076 6.66664 15 8.15903 15 9.99998C15 11.8409 13.5076 13.3333 11.6667 13.3333V11.6666C12.5871 11.6666 13.3333 10.9205 13.3333 9.99998C13.3333 9.0795 12.5871 8.33331 11.6667 8.33331V6.66664ZM11.6667 3.33331C15.3486 3.33331 18.3333 6.31808 18.3333 9.99998C18.3333 13.6819 15.3486 16.6666 11.6667 16.6666V15C14.4281 15 16.6667 12.7614 16.6667 9.99998C16.6667 7.23855 14.4281 4.99998 11.6667 4.99998V3.33331Z" fill="white"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -12,12 +12,13 @@ export type Props = {
|
||||
/**
|
||||
* String or component that will be rendered as the label itself.
|
||||
*/
|
||||
text: string
|
||||
label: string
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstract class for the {@code Label} component.
|
||||
* Abstract class for the {@code CircularLabel} component.
|
||||
*/
|
||||
export default class Label<P: Props, S: *>
|
||||
export default class AbstractCircularLabel<P: Props, S: *>
|
||||
extends Component<P, S> {
|
||||
|
||||
}
|
||||
@@ -5,9 +5,9 @@ import { Animated, Text } from 'react-native';
|
||||
import Icon from '../../icons/components/Icon';
|
||||
import { combineStyles, type StyleType } from '../../styles';
|
||||
|
||||
import AbstractLabel, {
|
||||
import AbstractCircularLabel, {
|
||||
type Props as AbstractProps
|
||||
} from './AbstractLabel';
|
||||
} from './AbstractCircularLabel';
|
||||
import styles from './styles';
|
||||
|
||||
/**
|
||||
@@ -48,14 +48,14 @@ type State = {
|
||||
* Renders a circular indicator to be used for status icons, such as recording
|
||||
* on, audio-only conference, video quality and similar.
|
||||
*/
|
||||
export default class Label extends AbstractLabel<Props, State> {
|
||||
export default class CircularLabel extends AbstractCircularLabel<Props, State> {
|
||||
/**
|
||||
* A reference to the started animation of this label.
|
||||
*/
|
||||
animationReference: Object;
|
||||
|
||||
/**
|
||||
* Instantiates a new instance of {@code Label}.
|
||||
* Instantiates a new instance of {@code CircularLabel}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
@@ -91,7 +91,7 @@ export default class Label extends AbstractLabel<Props, State> {
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { icon, text, status, style } = this.props;
|
||||
const { icon, label, status, style } = this.props;
|
||||
|
||||
let extraStyle = null;
|
||||
|
||||
@@ -106,18 +106,24 @@ export default class Label extends AbstractLabel<Props, State> {
|
||||
break;
|
||||
}
|
||||
|
||||
const labelComponent = icon
|
||||
? (
|
||||
<Icon
|
||||
src = { icon }
|
||||
style = { styles.indicatorIcon } />
|
||||
) : (
|
||||
<Text style = { styles.indicatorText }>
|
||||
{ label }
|
||||
</Text>
|
||||
);
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
style = { [
|
||||
combineStyles(styles.labelContainer, style),
|
||||
combineStyles(styles.indicatorContainer, style),
|
||||
extraStyle
|
||||
] }>
|
||||
{ icon && <Icon
|
||||
size = '18'
|
||||
src = { icon } /> }
|
||||
{ text && <Text style = { styles.labelText }>
|
||||
{ text }
|
||||
</Text>}
|
||||
{ labelComponent }
|
||||
</Animated.View>
|
||||
);
|
||||
}
|
||||
68
react/features/base/label/components/CircularLabel.web.js
Normal file
68
react/features/base/label/components/CircularLabel.web.js
Normal file
@@ -0,0 +1,68 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Icon from '../../icons/components/Icon';
|
||||
|
||||
import AbstractCircularLabel, {
|
||||
type Props as AbstractProps
|
||||
} from './AbstractCircularLabel';
|
||||
|
||||
type Props = AbstractProps & {
|
||||
|
||||
/**
|
||||
* Additional CSS class names to add to the root of {@code CircularLabel}.
|
||||
*/
|
||||
className: string,
|
||||
|
||||
/**
|
||||
* HTML ID attribute to add to the root of {@code CircularLabel}.
|
||||
*/
|
||||
id: string
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* React Component for showing short text in a circle.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
export default class CircularLabel extends AbstractCircularLabel<Props, {}> {
|
||||
/**
|
||||
* Default values for {@code CircularLabel} component's properties.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static defaultProps = {
|
||||
className: ''
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const {
|
||||
className,
|
||||
icon,
|
||||
id,
|
||||
label
|
||||
} = this.props;
|
||||
|
||||
const labelComponent = icon
|
||||
? (
|
||||
<Icon
|
||||
src = { icon } />
|
||||
) : label;
|
||||
|
||||
return (
|
||||
<div
|
||||
className = { `circular-label ${className}` }
|
||||
id = { id }>
|
||||
{ labelComponent }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Animated, Text, View } from 'react-native';
|
||||
|
||||
import styles, { DEFAULT_COLOR } from './styles';
|
||||
import styles, { DEFAULT_COLOR, LABEL_MARGIN, LABEL_SIZE } from './styles';
|
||||
|
||||
export type Props = {
|
||||
|
||||
@@ -19,9 +19,21 @@ type State = {
|
||||
/**
|
||||
* The opacity animation Object.
|
||||
*/
|
||||
opacityAnimation: Object
|
||||
opacityAnimation: Object,
|
||||
|
||||
/**
|
||||
* A boolean to decide to show or not show the arrow. This is required as
|
||||
* we can't easily animate this transformed Component so we render it once
|
||||
* the animation is done.
|
||||
*/
|
||||
showArrow: boolean
|
||||
};
|
||||
|
||||
/**
|
||||
* Offset to the arrow to be rendered in the right position.
|
||||
*/
|
||||
const ARROW_OFFSET = 0;
|
||||
|
||||
/**
|
||||
* A react {@code Component} that implements an expanded label as tooltip-like
|
||||
* component to explain the meaning of the {@code Label}.
|
||||
@@ -36,7 +48,8 @@ export default class ExpandedLabel<P: Props> extends Component<P, State> {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
opacityAnimation: new Animated.Value(0)
|
||||
opacityAnimation: new Animated.Value(0),
|
||||
showArrow: false
|
||||
};
|
||||
}
|
||||
|
||||
@@ -50,7 +63,11 @@ export default class ExpandedLabel<P: Props> extends Component<P, State> {
|
||||
toValue: 1,
|
||||
velocity: 1,
|
||||
useNativeDriver: true
|
||||
}).start();
|
||||
}).start(({ finished }) => {
|
||||
finished && this.setState({
|
||||
showArrow: true
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -59,12 +76,32 @@ export default class ExpandedLabel<P: Props> extends Component<P, State> {
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const arrowPosition
|
||||
= this.props.parentPosition - LABEL_MARGIN - (LABEL_SIZE / 2);
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
style = { [ styles.expandedLabelContainer, { opacity: this.state.opacityAnimation } ] }>
|
||||
style = { [
|
||||
styles.expandedLabelWrapper,
|
||||
{
|
||||
opacity: this.state.opacityAnimation
|
||||
}
|
||||
] } >
|
||||
<View
|
||||
style = { [ styles.expandedLabelTextContainer,
|
||||
{ backgroundColor: this._getColor() || DEFAULT_COLOR } ] }>
|
||||
style = { [
|
||||
styles.expandedLabelArrow,
|
||||
{
|
||||
backgroundColor: this._getColor() || DEFAULT_COLOR,
|
||||
marginRight: arrowPosition + ARROW_OFFSET
|
||||
}
|
||||
] } />
|
||||
<View
|
||||
style = { [
|
||||
styles.expandedLabelContainer,
|
||||
{
|
||||
backgroundColor: this._getColor() || DEFAULT_COLOR
|
||||
}
|
||||
] }>
|
||||
<Text style = { styles.expandedLabelText }>
|
||||
{ this._getLabel() }
|
||||
</Text>
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import Icon from '../../icons/components/Icon';
|
||||
|
||||
import AbstractLabel, {
|
||||
type Props as AbstractProps
|
||||
} from './AbstractLabel';
|
||||
|
||||
type Props = AbstractProps & {
|
||||
|
||||
/**
|
||||
* Additional CSS class names to add to the root of {@code Label}.
|
||||
*/
|
||||
className: string,
|
||||
|
||||
/**
|
||||
* HTML ID attribute to add to the root of {@code Label}.
|
||||
*/
|
||||
id: string
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* React Component for showing short text in a circle.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
export default class Label extends AbstractLabel<Props, *> {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const {
|
||||
className,
|
||||
icon,
|
||||
id,
|
||||
text
|
||||
} = this.props;
|
||||
|
||||
const labelClassName = icon ? 'label-text-with-icon' : '';
|
||||
|
||||
return (
|
||||
<div
|
||||
className = { `label ${className}` }
|
||||
id = { id }>
|
||||
{ icon && <Icon
|
||||
size = '16'
|
||||
src = { icon } /> }
|
||||
{ text && <span className = { labelClassName }>{text}</span> }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1,2 @@
|
||||
export { default as Label } from './Label';
|
||||
export { default as CircularLabel } from './CircularLabel';
|
||||
export { default as ExpandedLabel } from './ExpandedLabel';
|
||||
|
||||
@@ -1,65 +1,76 @@
|
||||
// @flow
|
||||
|
||||
import { ColorPalette } from '../../styles';
|
||||
import { BoxModel, ColorPalette } from '../../styles';
|
||||
|
||||
/**
|
||||
* The default color of the {@code Label} and {@code ExpandedLabel}.
|
||||
*/
|
||||
export const DEFAULT_COLOR = '#36383C';
|
||||
export const DEFAULT_COLOR = '#808080';
|
||||
|
||||
/**
|
||||
* Margin of the {@Label} - to be reused when rendering the
|
||||
* {@code ExpandedLabel}.
|
||||
*/
|
||||
export const LABEL_MARGIN = 8;
|
||||
export const LABEL_MARGIN = 5;
|
||||
|
||||
/**
|
||||
* Size of the {@Label} - to be reused when rendering the
|
||||
* {@code ExpandedLabel}.
|
||||
*/
|
||||
export const LABEL_SIZE = 28;
|
||||
export const LABEL_SIZE = 36;
|
||||
|
||||
/**
|
||||
* The styles of the native base/label feature.
|
||||
*/
|
||||
export default {
|
||||
expandedLabelContainer: {
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 36,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center'
|
||||
|
||||
expandedLabelArrow: {
|
||||
backgroundColor: ColorPalette.blue,
|
||||
height: 15,
|
||||
transform: [ { rotate: '45deg' }, { translateX: 10 } ],
|
||||
width: 15
|
||||
},
|
||||
|
||||
expandedLabelTextContainer: {
|
||||
borderRadius: 3,
|
||||
paddingHorizontal: LABEL_MARGIN,
|
||||
paddingVertical: LABEL_MARGIN / 2
|
||||
expandedLabelContainer: {
|
||||
backgroundColor: ColorPalette.blue,
|
||||
borderColor: ColorPalette.blue,
|
||||
borderRadius: 6,
|
||||
marginHorizontal: BoxModel.margin,
|
||||
padding: BoxModel.padding
|
||||
},
|
||||
|
||||
expandedLabelText: {
|
||||
color: ColorPalette.white
|
||||
},
|
||||
|
||||
expandedLabelWrapper: {
|
||||
alignItems: 'flex-end',
|
||||
flexDirection: 'column'
|
||||
},
|
||||
|
||||
/**
|
||||
* The outermost view.
|
||||
*/
|
||||
labelContainer: {
|
||||
alignItems: 'space-between',
|
||||
indicatorContainer: {
|
||||
alignItems: 'center',
|
||||
backgroundColor: DEFAULT_COLOR,
|
||||
borderRadius: 3,
|
||||
borderRadius: LABEL_SIZE / 2,
|
||||
borderWidth: 0,
|
||||
flex: 0,
|
||||
height: LABEL_SIZE,
|
||||
justifyContent: 'center',
|
||||
marginLeft: LABEL_MARGIN,
|
||||
marginBottom: LABEL_MARGIN,
|
||||
paddingHorizontal: 8
|
||||
marginHorizontal: LABEL_MARGIN,
|
||||
opacity: 0.6,
|
||||
width: LABEL_SIZE
|
||||
},
|
||||
|
||||
labelText: {
|
||||
indicatorIcon: {
|
||||
fontSize: 24
|
||||
},
|
||||
|
||||
indicatorText: {
|
||||
color: ColorPalette.white,
|
||||
fontSize: 12
|
||||
fontSize: 10
|
||||
},
|
||||
|
||||
labelOff: {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @flow
|
||||
|
||||
import React, { PureComponent } from 'react';
|
||||
import { KeyboardAvoidingView, Platform, SafeAreaView } from 'react-native';
|
||||
import { KeyboardAvoidingView, SafeAreaView } from 'react-native';
|
||||
|
||||
import { ColorSchemeRegistry } from '../../color-scheme';
|
||||
import { HeaderWithNavigation, SlidingView } from '../../react';
|
||||
@@ -106,12 +106,7 @@ class JitsiModal extends PureComponent<Props> {
|
||||
position = { position }
|
||||
show = { _show }>
|
||||
<KeyboardAvoidingView
|
||||
behavior =
|
||||
{
|
||||
Platform.OS === 'ios'
|
||||
? 'padding' : 'height'
|
||||
}
|
||||
enabled = { true }
|
||||
behavior = 'height'
|
||||
style = { [
|
||||
_headerStyles.page,
|
||||
_styles.page,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import UIEvents from '../../../../service/UI/UIEvents';
|
||||
import { toggleE2EE } from '../../e2ee/actions';
|
||||
import { NOTIFICATION_TIMEOUT, showNotification } from '../../notifications';
|
||||
import { CALLING, INVITED } from '../../presence-status';
|
||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../app';
|
||||
@@ -208,7 +207,7 @@ StateListenerRegistry.register(
|
||||
(conference, store) => {
|
||||
if (conference) {
|
||||
const propertyHandlers = {
|
||||
'e2ee.enabled': (participant, value) => _e2eeUpdated(store, conference, participant.getId(), value),
|
||||
'e2eeEnabled': (participant, value) => _e2eeUpdated(store, conference, participant.getId(), value),
|
||||
'features_e2ee': (participant, value) =>
|
||||
store.dispatch(participantUpdated({
|
||||
conference,
|
||||
@@ -271,16 +270,12 @@ StateListenerRegistry.register(
|
||||
* @param {Function} dispatch - The Redux dispatch function.
|
||||
* @param {Object} conference - The conference for which we got an update.
|
||||
* @param {string} participantId - The ID of the participant from which we got an update.
|
||||
* @param {boolean} newValue - The new value of the E2EE enabled status.
|
||||
* @param {boolean} newValue - The new value of the E2EE enabled status.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _e2eeUpdated({ dispatch }, conference, participantId, newValue) {
|
||||
const e2eeEnabled = newValue === 'true';
|
||||
|
||||
if (e2eeEnabled) {
|
||||
dispatch(toggleE2EE(e2eeEnabled));
|
||||
}
|
||||
|
||||
dispatch(participantUpdated({
|
||||
conference,
|
||||
id: participantId,
|
||||
@@ -388,7 +383,7 @@ function _maybePlaySounds({ getState, dispatch }, action) {
|
||||
*/
|
||||
function _participantJoinedOrUpdated(store, next, action) {
|
||||
const { dispatch, getState } = store;
|
||||
const { participant: { avatarURL, email, id, local, name, raisedHand } } = action;
|
||||
const { participant: { avatarURL, e2eeEnabled, email, id, local, name, raisedHand } } = action;
|
||||
|
||||
// Send an external update of the local participant's raised hand state
|
||||
// if a new raised hand state is defined in the action.
|
||||
@@ -403,6 +398,16 @@ function _participantJoinedOrUpdated(store, next, action) {
|
||||
}
|
||||
}
|
||||
|
||||
// Send an external update of the local participant's E2EE enabled state
|
||||
// if a new state is defined in the action.
|
||||
if (typeof e2eeEnabled !== 'undefined') {
|
||||
if (local) {
|
||||
const { conference } = getState()['features/base/conference'];
|
||||
|
||||
conference && conference.setLocalParticipantProperty('e2eeEnabled', e2eeEnabled);
|
||||
}
|
||||
}
|
||||
|
||||
// Allow the redux update to go through and compare the old avatar
|
||||
// to the new avatar and emit out change events if necessary.
|
||||
const result = next(action);
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { AudioSettingsButton, VideoSettingsButton } from '../../../../toolbox/components/web';
|
||||
import { VideoBackgroundButton } from '../../../../virtual-background';
|
||||
import { checkBlurSupport } from '../../../../virtual-background/functions';
|
||||
import { Avatar } from '../../../avatar';
|
||||
import { allowUrlSharing } from '../../functions';
|
||||
|
||||
@@ -116,7 +114,6 @@ export default class PreMeetingScreen extends PureComponent<Props> {
|
||||
<div className = 'toolbox-content-items'>
|
||||
<AudioSettingsButton visible = { true } />
|
||||
<VideoSettingsButton visible = { true } />
|
||||
<VideoBackgroundButton visible = { checkBlurSupport() } />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -257,16 +257,13 @@ export function showNoDataFromSourceVideoError(jitsiTrack) {
|
||||
* Signals that the local participant is ending screensharing or beginning the
|
||||
* screensharing flow.
|
||||
*
|
||||
* @param {boolean} audioOnly - Only share system audio.
|
||||
* @returns {{
|
||||
* type: TOGGLE_SCREENSHARING,
|
||||
* audioOnly: boolean
|
||||
* }}
|
||||
*/
|
||||
export function toggleScreensharing(audioOnly = false) {
|
||||
export function toggleScreensharing() {
|
||||
return {
|
||||
type: TOGGLE_SCREENSHARING,
|
||||
audioOnly
|
||||
type: TOGGLE_SCREENSHARING
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
|
||||
case TOGGLE_SCREENSHARING:
|
||||
if (typeof APP === 'object') {
|
||||
APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING, action.audioOnly);
|
||||
APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
130
react/features/conference/components/AbstractLabels.js
Normal file
130
react/features/conference/components/AbstractLabels.js
Normal file
@@ -0,0 +1,130 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { E2EELabel } from '../../e2ee';
|
||||
import { isFilmstripVisible } from '../../filmstrip';
|
||||
import { LocalRecordingLabel } from '../../local-recording';
|
||||
import { RecordingLabel } from '../../recording';
|
||||
import { TranscribingLabel } from '../../transcribing';
|
||||
import { shouldDisplayTileView } from '../../video-layout';
|
||||
import { VideoQualityLabel } from '../../video-quality';
|
||||
|
||||
import { InsecureRoomNameLabel } from '.';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link AbstractLabels}.
|
||||
*/
|
||||
export type Props = {
|
||||
|
||||
/**
|
||||
* Whether the filmstrip is displayed with remote videos. Used to determine
|
||||
* display classes to set.
|
||||
*/
|
||||
_filmstripVisible: boolean,
|
||||
|
||||
/**
|
||||
* Whether the video quality label should be displayed.
|
||||
*/
|
||||
_showVideoQualityLabel: boolean
|
||||
};
|
||||
|
||||
/**
|
||||
* A container to hold video status labels, including recording status and
|
||||
* current large video quality.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
export default class AbstractLabels<P: Props, S> extends Component<P, S> {
|
||||
/**
|
||||
* Renders the {@code E2EELabel}.
|
||||
*
|
||||
* @protected
|
||||
* @returns {React$Element}
|
||||
*/
|
||||
_renderE2EELabel() {
|
||||
return (
|
||||
<E2EELabel />
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the {@code LocalRecordingLabel}.
|
||||
*
|
||||
* @protected
|
||||
* @returns {React$Element}
|
||||
*/
|
||||
_renderLocalRecordingLabel() {
|
||||
return (
|
||||
<LocalRecordingLabel />
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the {@code RecordingLabel} that is platform independent.
|
||||
*
|
||||
* @param {string} mode - The recording mode that this label is rendered
|
||||
* for.
|
||||
* @protected
|
||||
* @returns {React$Element}
|
||||
*/
|
||||
_renderRecordingLabel(mode: string) {
|
||||
return (
|
||||
<RecordingLabel mode = { mode } />
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the {@code TranscribingLabel}.
|
||||
*
|
||||
* @protected
|
||||
* @returns {React$Element}
|
||||
*/
|
||||
_renderTranscribingLabel() {
|
||||
return (
|
||||
<TranscribingLabel />
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the {@code InsecureRoomNameLabel}.
|
||||
*
|
||||
* @protected
|
||||
* @returns {React$Element}
|
||||
*/
|
||||
_renderInsecureRoomNameLabel() {
|
||||
return (
|
||||
<InsecureRoomNameLabel />
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the {@code VideoQualityLabel} that is platform independent.
|
||||
*
|
||||
* @protected
|
||||
* @returns {React$Element}
|
||||
*/
|
||||
_renderVideoQualityLabel() {
|
||||
return (
|
||||
<VideoQualityLabel />
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the associated props of the {@link Labels}
|
||||
* {@code Component}.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @private
|
||||
* @returns {{
|
||||
* _filmstripVisible: boolean,
|
||||
* _showVideoQualityLabel: boolean
|
||||
* }}
|
||||
*/
|
||||
export function _abstractMapStateToProps(state: Object) {
|
||||
return {
|
||||
_filmstripVisible: isFilmstripVisible(state),
|
||||
_showVideoQualityLabel: !shouldDisplayTileView(state)
|
||||
};
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { IconWarning } from '../../../base/icons';
|
||||
import { Label } from '../../../base/label';
|
||||
import { CircularLabel } from '../../../base/label';
|
||||
import { connect } from '../../../base/redux';
|
||||
import AbstractInsecureRoomNameLabel, { _mapStateToProps } from '../AbstractInsecureRoomNameLabel';
|
||||
|
||||
@@ -20,7 +20,7 @@ class InsecureRoomNameLabel extends AbstractInsecureRoomNameLabel {
|
||||
*/
|
||||
_render() {
|
||||
return (
|
||||
<Label
|
||||
<CircularLabel
|
||||
icon = { IconWarning }
|
||||
style = { styles.insecureRoomNameLabel } />
|
||||
);
|
||||
|
||||
@@ -1,22 +1,80 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import React from 'react';
|
||||
import { TouchableOpacity, View } from 'react-native';
|
||||
|
||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||
import { RecordingLabel, RecordingExpandedLabel } from '../../../recording';
|
||||
import { TranscribingExpandedLabel, TranscribingLabel } from '../../../transcribing';
|
||||
import { VideoQualityExpandedLabel, VideoQualityLabel } from '../../../video-quality';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { ASPECT_RATIO_WIDE } from '../../../base/responsive-ui/constants';
|
||||
import {
|
||||
RecordingExpandedLabel
|
||||
} from '../../../recording';
|
||||
import { TranscribingExpandedLabel } from '../../../transcribing';
|
||||
import { shouldDisplayTileView } from '../../../video-layout';
|
||||
import { VideoQualityExpandedLabel } from '../../../video-quality';
|
||||
import { shouldDisplayNotifications } from '../../functions';
|
||||
import AbstractLabels, {
|
||||
_abstractMapStateToProps,
|
||||
type Props as AbstractLabelsProps
|
||||
} from '../AbstractLabels';
|
||||
|
||||
import InsecureRoomNameExpandedLabel from './InsecureRoomNameExpandedLabel';
|
||||
import styles from './styles';
|
||||
|
||||
import { InsecureRoomNameLabel } from '.';
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link Labels}.
|
||||
*/
|
||||
type Props = AbstractLabelsProps & {
|
||||
|
||||
type Props = {}
|
||||
/**
|
||||
* Application's aspect ratio.
|
||||
*/
|
||||
_aspectRatio: Symbol,
|
||||
|
||||
/**
|
||||
* True if tile view is being diaplyed, false otherwise.
|
||||
*/
|
||||
_shouldDisplayTileView: boolean,
|
||||
|
||||
/**
|
||||
* True if the labels should be visible, false otherwise.
|
||||
*/
|
||||
_visible: boolean,
|
||||
|
||||
/**
|
||||
* Function to translate i18n labels.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
|
||||
type State = {
|
||||
|
||||
/**
|
||||
* Layout object of the outermost container. For structure please see:
|
||||
* https://facebook.github.io/react-native/docs/view#onlayout
|
||||
*/
|
||||
containerLayout: ?Object,
|
||||
|
||||
/**
|
||||
* Layout objects of the individual labels. This data type contains the same
|
||||
* structure as the layout is defined here:
|
||||
* https://facebook.github.io/react-native/docs/view#onlayout
|
||||
* but keyed with the ID of the label its layout it contains. E.g.
|
||||
*
|
||||
* {
|
||||
* transcribing: {
|
||||
* { layout: { x, y, width, height } }
|
||||
* },
|
||||
* ...
|
||||
* }
|
||||
*/
|
||||
labelLayouts: Object,
|
||||
|
||||
/**
|
||||
* Position of the label to render the {@code ExpandedLabel} to.
|
||||
*/
|
||||
parentPosition: ?number,
|
||||
|
||||
/**
|
||||
* String to show which {@code ExpandedLabel} to be shown. (Equals to the
|
||||
* label IDs below.)
|
||||
@@ -30,33 +88,26 @@ const LABEL_ID_STREAMING = 'streaming';
|
||||
const LABEL_ID_TRANSCRIBING = 'transcribing';
|
||||
const LABEL_ID_INSECURE_ROOM_NAME = 'insecure-room-name';
|
||||
|
||||
const LabelHitSlop = {
|
||||
top: 10,
|
||||
bottom: 10,
|
||||
left: 0,
|
||||
right: 0
|
||||
};
|
||||
|
||||
/**
|
||||
* The {@code ExpandedLabel} components to be rendered for the individual
|
||||
* {@code Label}s.
|
||||
*/
|
||||
const EXPANDED_LABELS = {
|
||||
[LABEL_ID_QUALITY]: VideoQualityExpandedLabel,
|
||||
[LABEL_ID_RECORDING]: {
|
||||
quality: VideoQualityExpandedLabel,
|
||||
recording: {
|
||||
component: RecordingExpandedLabel,
|
||||
props: {
|
||||
mode: JitsiRecordingConstants.mode.FILE
|
||||
}
|
||||
},
|
||||
[LABEL_ID_STREAMING]: {
|
||||
streaming: {
|
||||
component: RecordingExpandedLabel,
|
||||
props: {
|
||||
mode: JitsiRecordingConstants.mode.STREAM
|
||||
}
|
||||
},
|
||||
[LABEL_ID_TRANSCRIBING]: TranscribingExpandedLabel,
|
||||
[LABEL_ID_INSECURE_ROOM_NAME]: InsecureRoomNameExpandedLabel
|
||||
transcribing: TranscribingExpandedLabel,
|
||||
'insecure-room-name': InsecureRoomNameExpandedLabel
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -67,7 +118,7 @@ const EXPANDED_LABEL_TIMEOUT = 5000;
|
||||
/**
|
||||
* A container that renders the conference indicators, if any.
|
||||
*/
|
||||
class Labels extends Component<Props, State> {
|
||||
class Labels extends AbstractLabels<Props, State> {
|
||||
/**
|
||||
* Timeout for the expanded labels to disappear.
|
||||
*/
|
||||
@@ -80,9 +131,15 @@ class Labels extends Component<Props, State> {
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
containerLayout: undefined,
|
||||
labelLayouts: {},
|
||||
parentPosition: undefined,
|
||||
visibleExpandedLabel: undefined
|
||||
};
|
||||
|
||||
this._onTopViewLayout = this._onTopViewLayout.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,49 +157,110 @@ class Labels extends Component<Props, State> {
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { _aspectRatio, _filmstripVisible, _shouldDisplayTileView, _visible } = this.props;
|
||||
|
||||
if (!_visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const wide = _aspectRatio === ASPECT_RATIO_WIDE;
|
||||
|
||||
return (
|
||||
<>
|
||||
<View pointerEvents = 'box-none'>
|
||||
<View
|
||||
pointerEvents = 'box-none'
|
||||
style = { styles.indicatorContainer }>
|
||||
<TouchableOpacity
|
||||
hitSlop = { LabelHitSlop }
|
||||
onPress = { this._createOnPress(LABEL_ID_RECORDING) } >
|
||||
<RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
hitSlop = { LabelHitSlop }
|
||||
onPress = { this._createOnPress(LABEL_ID_STREAMING) } >
|
||||
<RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
hitSlop = { LabelHitSlop }
|
||||
onPress = {
|
||||
this._createOnPress(LABEL_ID_TRANSCRIBING)
|
||||
} >
|
||||
<TranscribingLabel />
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
hitSlop = { LabelHitSlop }
|
||||
onPress = {
|
||||
this._createOnPress(LABEL_ID_INSECURE_ROOM_NAME)
|
||||
} >
|
||||
<InsecureRoomNameLabel />
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
hitSlop = { LabelHitSlop }
|
||||
onPress = {
|
||||
this._createOnPress(LABEL_ID_QUALITY) } >
|
||||
<VideoQualityLabel />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
<View
|
||||
pointerEvents = 'box-none'
|
||||
style = { styles.labelWrapper }>
|
||||
<View
|
||||
onLayout = { this._onTopViewLayout }
|
||||
pointerEvents = 'box-none'
|
||||
style = { [
|
||||
styles.indicatorContainer,
|
||||
wide && _filmstripVisible && !_shouldDisplayTileView
|
||||
&& styles.indicatorContainerWide
|
||||
] }>
|
||||
<TouchableOpacity
|
||||
onLayout = { this._createOnLayout(LABEL_ID_RECORDING) }
|
||||
onPress = { this._createOnPress(LABEL_ID_RECORDING) } >
|
||||
{
|
||||
this._renderRecordingLabel(
|
||||
JitsiRecordingConstants.mode.FILE)
|
||||
}
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onLayout = { this._createOnLayout(LABEL_ID_STREAMING) }
|
||||
onPress = { this._createOnPress(LABEL_ID_STREAMING) } >
|
||||
{
|
||||
this._renderRecordingLabel(
|
||||
JitsiRecordingConstants.mode.STREAM)
|
||||
}
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onLayout = {
|
||||
this._createOnLayout(LABEL_ID_TRANSCRIBING)
|
||||
}
|
||||
onPress = {
|
||||
this._createOnPress(LABEL_ID_TRANSCRIBING)
|
||||
} >
|
||||
{
|
||||
this._renderTranscribingLabel()
|
||||
}
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onLayout = {
|
||||
this._createOnLayout(LABEL_ID_INSECURE_ROOM_NAME)
|
||||
}
|
||||
onPress = {
|
||||
this._createOnPress(LABEL_ID_INSECURE_ROOM_NAME)
|
||||
} >
|
||||
{
|
||||
this._renderInsecureRoomNameLabel()
|
||||
}
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
onLayout = {
|
||||
this._createOnLayout(LABEL_ID_QUALITY) }
|
||||
onPress = {
|
||||
this._createOnPress(LABEL_ID_QUALITY) } >
|
||||
{ this._renderVideoQualityLabel() }
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
{ this._renderExpandedLabel() }
|
||||
</>
|
||||
<View
|
||||
style = { [
|
||||
styles.indicatorContainer,
|
||||
wide && _filmstripVisible && !_shouldDisplayTileView
|
||||
&& styles.indicatorContainerWide
|
||||
] }>
|
||||
{
|
||||
this._renderExpandedLabel()
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a function to be invoked when the onLayout of the touchables are
|
||||
* triggered.
|
||||
*
|
||||
* @param {string} label - The identifier of the label that's onLayout is
|
||||
* triggered.
|
||||
* @returns {Function}
|
||||
*/
|
||||
_createOnLayout(label) {
|
||||
return ({ nativeEvent: { layout } }) => {
|
||||
const { labelLayouts } = this.state;
|
||||
const updatedLayout = {};
|
||||
|
||||
updatedLayout[label] = layout;
|
||||
|
||||
this.setState({
|
||||
labelLayouts: {
|
||||
...labelLayouts,
|
||||
...updatedLayout
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a function to be invoked when the onPress of the touchables are
|
||||
* triggered.
|
||||
@@ -153,47 +271,102 @@ class Labels extends Component<Props, State> {
|
||||
*/
|
||||
_createOnPress(label) {
|
||||
return () => {
|
||||
const {
|
||||
containerLayout,
|
||||
labelLayouts
|
||||
} = this.state;
|
||||
let { visibleExpandedLabel } = this.state;
|
||||
|
||||
visibleExpandedLabel
|
||||
= visibleExpandedLabel === label ? undefined : label;
|
||||
if (containerLayout) {
|
||||
const labelLayout = labelLayouts[label];
|
||||
|
||||
// This calculation has to be changed if the labels are not
|
||||
// positioned right anymore.
|
||||
const right = containerLayout.width - labelLayout.x;
|
||||
|
||||
clearTimeout(this.expandedLabelTimeout);
|
||||
this.setState({
|
||||
visibleExpandedLabel
|
||||
});
|
||||
= visibleExpandedLabel === label ? undefined : label;
|
||||
|
||||
if (visibleExpandedLabel) {
|
||||
this.expandedLabelTimeout = setTimeout(() => {
|
||||
this.setState({
|
||||
visibleExpandedLabel: undefined
|
||||
});
|
||||
}, EXPANDED_LABEL_TIMEOUT);
|
||||
clearTimeout(this.expandedLabelTimeout);
|
||||
this.setState({
|
||||
parentPosition: right,
|
||||
visibleExpandedLabel
|
||||
});
|
||||
|
||||
if (visibleExpandedLabel) {
|
||||
this.expandedLabelTimeout = setTimeout(() => {
|
||||
this.setState({
|
||||
visibleExpandedLabel: undefined
|
||||
});
|
||||
}, EXPANDED_LABEL_TIMEOUT);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
_onTopViewLayout: Object => void
|
||||
|
||||
/**
|
||||
* Invoked when the View containing the {@code Label}s is laid out.
|
||||
*
|
||||
* @param {Object} layout - The native layout object.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onTopViewLayout({ nativeEvent: { layout } }) {
|
||||
this.setState({
|
||||
containerLayout: layout
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Rendes the expanded (explaining) label for the label that was touched.
|
||||
*
|
||||
* @returns {React$Element}
|
||||
*/
|
||||
_renderExpandedLabel() {
|
||||
const { visibleExpandedLabel } = this.state;
|
||||
const { parentPosition, visibleExpandedLabel } = this.state;
|
||||
|
||||
if (visibleExpandedLabel) {
|
||||
const expandedLabel = EXPANDED_LABELS[visibleExpandedLabel];
|
||||
|
||||
if (expandedLabel) {
|
||||
const LabelComponent = expandedLabel.component || expandedLabel;
|
||||
const { props } = expandedLabel || {};
|
||||
const component = expandedLabel.component || expandedLabel;
|
||||
const expandedLabelProps = expandedLabel.props || {};
|
||||
|
||||
return <LabelComponent { ...props } />;
|
||||
return React.createElement(component, {
|
||||
...expandedLabelProps,
|
||||
parentPosition
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
_renderRecordingLabel: string => React$Element<any>;
|
||||
|
||||
_renderTranscribingLabel: () => React$Element<any>;
|
||||
|
||||
_renderInsecureRoomNameLabel: () => React$Element<any>;
|
||||
|
||||
_renderVideoQualityLabel: () => React$Element<any>;
|
||||
}
|
||||
|
||||
export default Labels;
|
||||
/**
|
||||
* Maps (parts of) the redux state to the associated
|
||||
* {@code Labels}'s props.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @private
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
return {
|
||||
..._abstractMapStateToProps(state),
|
||||
_aspectRatio: state['features/base/responsive-ui'].aspectRatio,
|
||||
_shouldDisplayTileView: shouldDisplayTileView(state),
|
||||
_visible: !shouldDisplayNotifications(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(Labels);
|
||||
|
||||
@@ -57,9 +57,12 @@ const NavigationBar = (props: Props) => {
|
||||
styles = { styles.navBarButton } />
|
||||
<View
|
||||
pointerEvents = 'box-none'
|
||||
style = { styles.roomNameWrapper }>
|
||||
{
|
||||
props._meetingNameEnabled
|
||||
style = { styles.roomNameContainer }>
|
||||
<View
|
||||
pointerEvents = 'box-none'
|
||||
style = { styles.roomNameWrapper }>
|
||||
{
|
||||
props._meetingNameEnabled
|
||||
&& <View style = { styles.roomNameView }>
|
||||
<Text
|
||||
numberOfLines = { 1 }
|
||||
@@ -67,13 +70,14 @@ const NavigationBar = (props: Props) => {
|
||||
{ props._meetingName }
|
||||
</Text>
|
||||
</View>
|
||||
}
|
||||
{
|
||||
props._conferenceTimerEnabled
|
||||
}
|
||||
{
|
||||
props._conferenceTimerEnabled
|
||||
&& <View style = { styles.roomTimerView }>
|
||||
<ConferenceTimer textStyle = { styles.roomTimer } />
|
||||
</View>
|
||||
}
|
||||
}
|
||||
</View>
|
||||
<Labels />
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ColorSchemeRegistry, schemeColor } from '../../../base/color-scheme';
|
||||
import { BoxModel, ColorPalette, fixAndroidViewClipping } from '../../../base/styles';
|
||||
import { FILMSTRIP_SIZE } from '../../../filmstrip';
|
||||
|
||||
export const INSECURE_ROOM_NAME_LABEL_COLOR = ColorPalette.warning;
|
||||
|
||||
@@ -26,9 +27,23 @@ export default {
|
||||
*/
|
||||
indicatorContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row'
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end'
|
||||
},
|
||||
|
||||
/**
|
||||
* Indicator container for wide aspect ratio.
|
||||
*/
|
||||
indicatorContainerWide: {
|
||||
marginRight: FILMSTRIP_SIZE + BoxModel.margin
|
||||
},
|
||||
|
||||
labelWrapper: {
|
||||
flexDirection: 'column',
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0
|
||||
},
|
||||
|
||||
lonelyButton: {
|
||||
alignItems: 'center',
|
||||
@@ -62,6 +77,14 @@ export default {
|
||||
underlayColor: 'transparent'
|
||||
},
|
||||
|
||||
navBarContainer: {
|
||||
flexDirection: 'column',
|
||||
left: 0,
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0
|
||||
},
|
||||
|
||||
navBarSafeView: {
|
||||
left: 0,
|
||||
position: 'absolute',
|
||||
@@ -74,15 +97,14 @@ export default {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
height: 44,
|
||||
justifyContent: 'center',
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: 14
|
||||
},
|
||||
|
||||
roomTimer: {
|
||||
color: ColorPalette.white,
|
||||
fontSize: 12,
|
||||
fontWeight: '400',
|
||||
paddingHorizontal: 8
|
||||
fontWeight: '400'
|
||||
},
|
||||
|
||||
roomTimerView: {
|
||||
@@ -91,7 +113,7 @@ export default {
|
||||
borderTopRightRadius: 3,
|
||||
height: 28,
|
||||
justifyContent: 'center',
|
||||
minWidth: 50
|
||||
paddingHorizontal: 10
|
||||
},
|
||||
|
||||
roomName: {
|
||||
@@ -104,13 +126,21 @@ export default {
|
||||
backgroundColor: 'rgba(0,0,0,0.6)',
|
||||
borderBottomLeftRadius: 3,
|
||||
borderTopLeftRadius: 3,
|
||||
flexShrink: 1,
|
||||
height: 28,
|
||||
justifyContent: 'center',
|
||||
paddingHorizontal: 10
|
||||
},
|
||||
|
||||
roomNameContainer: {
|
||||
alignItems: 'center',
|
||||
left: 0,
|
||||
paddingHorizontal: 48,
|
||||
position: 'absolute',
|
||||
right: 0
|
||||
},
|
||||
|
||||
roomNameWrapper: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row'
|
||||
},
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
} from '../AbstractConference';
|
||||
import type { AbstractProps } from '../AbstractConference';
|
||||
|
||||
import ConferenceInfo from './ConferenceInfo';
|
||||
import Labels from './Labels';
|
||||
import { default as Notice } from './Notice';
|
||||
|
||||
declare var APP: Object;
|
||||
@@ -67,6 +67,11 @@ type Props = AbstractProps & {
|
||||
*/
|
||||
_backgroundAlpha: number,
|
||||
|
||||
/**
|
||||
* Whether the local participant is recording the conference.
|
||||
*/
|
||||
_iAmRecorder: boolean,
|
||||
|
||||
/**
|
||||
* Returns true if the 'lobby screen' is visible.
|
||||
*/
|
||||
@@ -178,10 +183,12 @@ class Conference extends AbstractConference<Props, *> {
|
||||
*/
|
||||
render() {
|
||||
const {
|
||||
_iAmRecorder,
|
||||
_isLobbyScreenVisible,
|
||||
_layoutClassName,
|
||||
_showPrejoin
|
||||
} = this.props;
|
||||
const hideLabels = _iAmRecorder;
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -189,13 +196,13 @@ class Conference extends AbstractConference<Props, *> {
|
||||
id = 'videoconference_page'
|
||||
onMouseMove = { this._onShowToolbar }
|
||||
ref = { this._setBackground }>
|
||||
<ConferenceInfo />
|
||||
|
||||
<Notice />
|
||||
<div id = 'videospace'>
|
||||
<LargeVideo />
|
||||
<KnockingParticipantList />
|
||||
<Filmstrip />
|
||||
{ hideLabels || <Labels /> }
|
||||
</div>
|
||||
|
||||
{ _showPrejoin || _isLobbyScreenVisible || <Toolbox /> }
|
||||
@@ -295,6 +302,7 @@ class Conference extends AbstractConference<Props, *> {
|
||||
function _mapStateToProps(state) {
|
||||
return {
|
||||
...abstractMapStateToProps(state),
|
||||
_iAmRecorder: state['features/base/config'].iAmRecorder,
|
||||
_backgroundAlpha: state['features/base/config'].backgroundAlpha,
|
||||
_isLobbyScreenVisible: state['features/base/dialog']?.component === LobbyScreen,
|
||||
_layoutClassName: LAYOUT_CLASSNAMES[getCurrentLayout(state)],
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
/* @flow */
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { getConferenceName } from '../../../base/conference/functions';
|
||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||
import { getParticipantCount } from '../../../base/participants/functions';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { E2EELabel } from '../../../e2ee';
|
||||
import { LocalRecordingLabel } from '../../../local-recording';
|
||||
import { RecordingLabel } from '../../../recording';
|
||||
import { isToolboxVisible } from '../../../toolbox/functions.web';
|
||||
import { TranscribingLabel } from '../../../transcribing';
|
||||
import { VideoQualityLabel } from '../../../video-quality';
|
||||
import ConferenceTimer from '../ConferenceTimer';
|
||||
|
||||
import ParticipantsCount from './ParticipantsCount';
|
||||
|
||||
import { InsecureRoomNameLabel } from '.';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link Subject}.
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Whether the info should span across the full width.
|
||||
*/
|
||||
_fullWidth: boolean,
|
||||
|
||||
/**
|
||||
* Whether the conference name and timer should be displayed or not.
|
||||
*/
|
||||
_hideConferenceNameAndTimer: boolean,
|
||||
|
||||
/**
|
||||
* Whether the conference timer should be shown or not.
|
||||
*/
|
||||
_hideConferenceTimer: boolean,
|
||||
|
||||
/**
|
||||
* Whether the participant count should be shown or not.
|
||||
*/
|
||||
_showParticipantCount: boolean,
|
||||
|
||||
/**
|
||||
* The subject or the of the conference.
|
||||
* Falls back to conference name.
|
||||
*/
|
||||
_subject: string,
|
||||
|
||||
/**
|
||||
* Indicates whether the component should be visible or not.
|
||||
*/
|
||||
_visible: boolean
|
||||
};
|
||||
|
||||
/**
|
||||
* The upper band of the meeing containing the conference name, timer and labels.
|
||||
*
|
||||
* @param {Object} props - The props of the component.
|
||||
* @returns {React$None}
|
||||
*/
|
||||
function ConferenceInfo(props: Props) {
|
||||
const {
|
||||
_hideConferenceNameAndTimer,
|
||||
_hideConferenceTimer,
|
||||
_showParticipantCount,
|
||||
_subject,
|
||||
_fullWidth,
|
||||
_visible
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<div className = { `subject ${_visible ? 'visible' : ''}` }>
|
||||
<div className = { `subject-info-container${_fullWidth ? ' subject-info-container--full-width' : ''}` }>
|
||||
{
|
||||
!_hideConferenceNameAndTimer
|
||||
&& <div className = 'subject-info'>
|
||||
{ _subject && <span className = 'subject-text'>{ _subject }</span>}
|
||||
{ !_hideConferenceTimer && <ConferenceTimer /> }
|
||||
</div>
|
||||
}
|
||||
{ _showParticipantCount && <ParticipantsCount /> }
|
||||
<E2EELabel />
|
||||
<RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
|
||||
<RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
|
||||
<LocalRecordingLabel />
|
||||
<TranscribingLabel />
|
||||
<VideoQualityLabel />
|
||||
<InsecureRoomNameLabel />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the Redux state to the associated
|
||||
* {@code Subject}'s props.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @private
|
||||
* @returns {{
|
||||
* _hideConferenceTimer: boolean,
|
||||
* _showParticipantCount: boolean,
|
||||
* _subject: string,
|
||||
* _visible: boolean
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
const participantCount = getParticipantCount(state);
|
||||
const { hideConferenceTimer, hideConferenceSubject, hideParticipantsStats } = state['features/base/config'];
|
||||
const { clientWidth } = state['features/base/responsive-ui'];
|
||||
|
||||
return {
|
||||
_hideConferenceNameAndTimer: clientWidth < 300,
|
||||
_hideConferenceTimer: Boolean(hideConferenceTimer),
|
||||
_fullWidth: state['features/video-layout'].tileViewEnabled,
|
||||
_showParticipantCount: participantCount > 2 && !hideParticipantsStats,
|
||||
_subject: hideConferenceSubject ? '' : getConferenceName(state),
|
||||
_visible: isToolboxVisible(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(ConferenceInfo);
|
||||
@@ -14,6 +14,6 @@ import React from 'react';
|
||||
*/
|
||||
export default function renderConferenceTimer(timerValue: string, textStyle: Object) {
|
||||
return (
|
||||
<span className = 'subject-timer'>{ timerValue }</span>
|
||||
<span className = 'subject-conference-timer' >{ timerValue }</span>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import React from 'react';
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { IconWarning } from '../../../base/icons';
|
||||
import { Label } from '../../../base/label';
|
||||
import { CircularLabel } from '../../../base/label';
|
||||
import { connect } from '../../../base/redux';
|
||||
import AbstractInsecureRoomNameLabel, { _mapStateToProps } from '../AbstractInsecureRoomNameLabel';
|
||||
|
||||
@@ -22,9 +22,9 @@ class InsecureRoomNameLabel extends AbstractInsecureRoomNameLabel {
|
||||
return (
|
||||
<Tooltip
|
||||
content = { this.props.t('security.insecureRoomNameWarning') }
|
||||
position = 'bottom'>
|
||||
<Label
|
||||
className = 'label--red'
|
||||
position = 'left'>
|
||||
<CircularLabel
|
||||
className = 'insecure'
|
||||
icon = { IconWarning } />
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
119
react/features/conference/components/web/Labels.js
Normal file
119
react/features/conference/components/web/Labels.js
Normal file
@@ -0,0 +1,119 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||
import { connect } from '../../../base/redux';
|
||||
import AbstractLabels, {
|
||||
_abstractMapStateToProps as _mapStateToProps,
|
||||
type Props
|
||||
} from '../AbstractLabels';
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} state of {@link Labels}.
|
||||
*/
|
||||
type State = {
|
||||
|
||||
/**
|
||||
* Whether or not the filmstrip was not visible but has transitioned in the
|
||||
* latest component update to visible. This boolean is used to set a class
|
||||
* for position animations.
|
||||
*
|
||||
* @type {boolean}
|
||||
*/
|
||||
filmstripBecomingVisible: boolean
|
||||
};
|
||||
|
||||
/**
|
||||
* A container to hold video status labels, including recording status and
|
||||
* current large video quality.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
class Labels extends AbstractLabels<Props, State> {
|
||||
/**
|
||||
* Updates the state for whether or not the filmstrip is transitioning to
|
||||
* a displayed state.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
static getDerivedStateFromProps(props: Props, prevState: State) {
|
||||
return {
|
||||
filmstripBecomingVisible: !prevState.filmstripBecomingVisible && props._filmstripVisible
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new {@code Labels} instance.
|
||||
*
|
||||
* @param {Object} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
filmstripBecomingVisible: false
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { _filmstripVisible } = this.props;
|
||||
const { filmstripBecomingVisible } = this.state;
|
||||
const { VIDEO_QUALITY_LABEL_DISABLED } = interfaceConfig;
|
||||
const className = `large-video-labels ${
|
||||
filmstripBecomingVisible ? 'opening' : ''} ${
|
||||
_filmstripVisible ? 'with-filmstrip' : 'without-filmstrip'}`;
|
||||
|
||||
return (
|
||||
<div className = { className } >
|
||||
{
|
||||
this._renderE2EELabel()
|
||||
}
|
||||
{
|
||||
this._renderRecordingLabel(
|
||||
JitsiRecordingConstants.mode.FILE)
|
||||
}
|
||||
{
|
||||
this._renderRecordingLabel(
|
||||
JitsiRecordingConstants.mode.STREAM)
|
||||
}
|
||||
{
|
||||
this._renderLocalRecordingLabel()
|
||||
}
|
||||
{
|
||||
this._renderTranscribingLabel()
|
||||
}
|
||||
{
|
||||
this.props._showVideoQualityLabel && !VIDEO_QUALITY_LABEL_DISABLED
|
||||
&& this._renderVideoQualityLabel()
|
||||
}
|
||||
{
|
||||
this._renderInsecureRoomNameLabel()
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
_renderE2EELabel: () => React$Element<*>;
|
||||
|
||||
_renderLocalRecordingLabel: () => React$Element<*>;
|
||||
|
||||
_renderRecordingLabel: string => React$Element<*>;
|
||||
|
||||
_renderTranscribingLabel: () => React$Element<*>;
|
||||
|
||||
_renderInsecureRoomNameLabel: () => React$Element<any>;
|
||||
|
||||
_renderVideoQualityLabel: () => React$Element<*>;
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(Labels);
|
||||
@@ -4,13 +4,10 @@ import React, { PureComponent } from 'react';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { openDialog } from '../../../base/dialog';
|
||||
import { IconUserGroups } from '../../../base/icons';
|
||||
import { Label } from '../../../base/label';
|
||||
import { getParticipantCount } from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { SpeakerStats } from '../../../speaker-stats';
|
||||
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link ParticipantsCount}.
|
||||
*/
|
||||
@@ -76,10 +73,10 @@ class ParticipantsCount extends PureComponent<Props> {
|
||||
<div
|
||||
className = 'participants-count'
|
||||
onClick = { this._onClick }>
|
||||
<Label
|
||||
className = 'label--white'
|
||||
icon = { IconUserGroups }
|
||||
text = { this.props.count } />
|
||||
<span className = 'participants-count-number'>
|
||||
{this.props.count}
|
||||
</span>
|
||||
<span className = 'participants-count-icon' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
103
react/features/conference/components/web/Subject.js
Normal file
103
react/features/conference/components/web/Subject.js
Normal file
@@ -0,0 +1,103 @@
|
||||
/* @flow */
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { getConferenceName } from '../../../base/conference/functions';
|
||||
import { getParticipantCount } from '../../../base/participants/functions';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { isToolboxVisible } from '../../../toolbox/functions.web';
|
||||
import ConferenceTimer from '../ConferenceTimer';
|
||||
|
||||
import ParticipantsCount from './ParticipantsCount';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link Subject}.
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Whether the conference timer should be shown or not.
|
||||
*/
|
||||
_hideConferenceTimer: Boolean,
|
||||
|
||||
/**
|
||||
* Whether the participant count should be shown or not.
|
||||
*/
|
||||
_showParticipantCount: boolean,
|
||||
|
||||
/**
|
||||
* Whether the conference subject should be shown or not.
|
||||
*/
|
||||
_showSubject: boolean,
|
||||
|
||||
/**
|
||||
* The subject or the of the conference.
|
||||
* Falls back to conference name.
|
||||
*/
|
||||
_subject: string,
|
||||
|
||||
/**
|
||||
* Indicates whether the component should be visible or not.
|
||||
*/
|
||||
_visible: boolean
|
||||
};
|
||||
|
||||
/**
|
||||
* Subject react component.
|
||||
*
|
||||
* @class Subject
|
||||
*/
|
||||
class Subject extends Component<Props> {
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { _hideConferenceTimer, _showParticipantCount, _showSubject, _subject, _visible } = this.props;
|
||||
let className = `subject ${_visible ? 'visible' : ''}`;
|
||||
|
||||
if (!_hideConferenceTimer || _showParticipantCount || _showSubject) {
|
||||
className += ' gradient';
|
||||
}
|
||||
|
||||
return (
|
||||
<div className = { className }>
|
||||
{ _showSubject && <span className = 'subject-text'>{ _subject }</span>}
|
||||
{ _showParticipantCount && <ParticipantsCount /> }
|
||||
{ !_hideConferenceTimer && <ConferenceTimer /> }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the Redux state to the associated
|
||||
* {@code Subject}'s props.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @private
|
||||
* @returns {{
|
||||
* _hideConferenceTimer: boolean,
|
||||
* _showParticipantCount: boolean,
|
||||
* _showSubject: boolean,
|
||||
* _subject: string,
|
||||
* _visible: boolean
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
const participantCount = getParticipantCount(state);
|
||||
const { hideConferenceTimer, hideConferenceSubject, hideParticipantsStats } = state['features/base/config'];
|
||||
|
||||
return {
|
||||
_hideConferenceTimer: Boolean(hideConferenceTimer),
|
||||
_showParticipantCount: participantCount > 2 && !hideParticipantsStats,
|
||||
_showSubject: !hideConferenceSubject,
|
||||
_subject: getConferenceName(state),
|
||||
_visible: isToolboxVisible(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(Subject);
|
||||
@@ -4,4 +4,4 @@ export { default as Conference } from './Conference';
|
||||
export { default as renderConferenceTimer } from './ConferenceTimerDisplay';
|
||||
export { default as InsecureRoomNameLabel } from './InsecureRoomNameLabel';
|
||||
export { default as InviteMore } from './InviteMore';
|
||||
export { default as ConferenceInfo } from './ConferenceInfo';
|
||||
export { default as Subject } from './Subject';
|
||||
|
||||
@@ -4,7 +4,7 @@ import React, { Component } from 'react';
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
import { IconE2EE } from '../../base/icons';
|
||||
import { Label } from '../../base/label';
|
||||
import { CircularLabel } from '../../base/label';
|
||||
import { connect } from '../../base/redux';
|
||||
import { Tooltip } from '../../base/tooltip';
|
||||
|
||||
@@ -32,9 +32,9 @@ class E2EELabel extends Component<Props> {
|
||||
return (
|
||||
<Tooltip
|
||||
content = { this.props.t('e2ee.labelToolTip') }
|
||||
position = { 'bottom' }>
|
||||
<Label
|
||||
className = 'label--green'
|
||||
position = { 'left' }>
|
||||
<CircularLabel
|
||||
className = 'e2ee'
|
||||
icon = { IconE2EE } />
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
// @flow
|
||||
|
||||
/**
|
||||
* The identifier of the sound to be played when e2ee is disabled.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
export const E2EE_OFF_SOUND_ID = 'E2EE_OFF_SOUND';
|
||||
|
||||
/**
|
||||
* The identifier of the sound to be played when e2ee is enabled.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
export const E2EE_ON_SOUND_ID = 'E2EE_ON_SOUND';
|
||||
@@ -1,16 +1,12 @@
|
||||
// @flow
|
||||
|
||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app';
|
||||
import { getCurrentConference } from '../base/conference';
|
||||
import { getLocalParticipant, participantUpdated } from '../base/participants';
|
||||
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
|
||||
import { playSound, registerSound, unregisterSound } from '../base/sounds';
|
||||
|
||||
import { TOGGLE_E2EE } from './actionTypes';
|
||||
import { toggleE2EE } from './actions';
|
||||
import { E2EE_OFF_SOUND_ID, E2EE_ON_SOUND_ID } from './constants';
|
||||
import logger from './logger';
|
||||
import { E2EE_OFF_SOUND_FILE, E2EE_ON_SOUND_FILE } from './sounds';
|
||||
|
||||
/**
|
||||
* Middleware that captures actions related to E2EE.
|
||||
@@ -20,25 +16,10 @@ import { E2EE_OFF_SOUND_FILE, E2EE_ON_SOUND_FILE } from './sounds';
|
||||
*/
|
||||
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
||||
switch (action.type) {
|
||||
case APP_WILL_MOUNT:
|
||||
dispatch(registerSound(
|
||||
E2EE_OFF_SOUND_ID,
|
||||
E2EE_OFF_SOUND_FILE));
|
||||
|
||||
dispatch(registerSound(
|
||||
E2EE_ON_SOUND_ID,
|
||||
E2EE_ON_SOUND_FILE));
|
||||
break;
|
||||
|
||||
case APP_WILL_UNMOUNT:
|
||||
dispatch(unregisterSound(E2EE_OFF_SOUND_ID));
|
||||
dispatch(unregisterSound(E2EE_ON_SOUND_ID));
|
||||
break;
|
||||
|
||||
case TOGGLE_E2EE: {
|
||||
const conference = getCurrentConference(getState);
|
||||
|
||||
if (conference && conference.isE2EEEnabled() !== action.enabled) {
|
||||
if (conference) {
|
||||
logger.debug(`E2EE will be ${action.enabled ? 'enabled' : 'disabled'}`);
|
||||
conference.toggleE2EE(action.enabled);
|
||||
|
||||
@@ -50,10 +31,6 @@ MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
|
||||
id: participant.id,
|
||||
local: true
|
||||
}));
|
||||
|
||||
const soundID = action.enabled ? E2EE_ON_SOUND_ID : E2EE_OFF_SOUND_ID;
|
||||
|
||||
dispatch(playSound(soundID));
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
// @flow
|
||||
|
||||
/**
|
||||
* The name of the bundled audio file which will be played when e2ee is disabled.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
export const E2EE_OFF_SOUND_FILE = 'e2eeOff.mp3';
|
||||
|
||||
/**
|
||||
* The name of the bundled audio file which will be played when e2ee is enabled.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
export const E2EE_ON_SOUND_FILE = 'e2eeOn.mp3';
|
||||
@@ -3,7 +3,7 @@
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { getInviteURL } from '../base/connection';
|
||||
import { getLocalParticipant, getParticipants } from '../base/participants';
|
||||
import { getParticipants } from '../base/participants';
|
||||
import { inviteVideoRooms } from '../videosipgw';
|
||||
|
||||
import {
|
||||
@@ -18,8 +18,7 @@ import {
|
||||
import {
|
||||
getDialInConferenceID,
|
||||
getDialInNumbers,
|
||||
invitePeopleAndChatRooms,
|
||||
inviteSipEndpoints
|
||||
invitePeopleAndChatRooms
|
||||
} from './functions';
|
||||
import logger from './logger';
|
||||
|
||||
@@ -103,10 +102,7 @@ export function invite(
|
||||
inviteServiceCallFlowsUrl
|
||||
} = state['features/base/config'];
|
||||
const inviteUrl = getInviteURL(state);
|
||||
const { sipInviteUrl } = state['features/base/config'];
|
||||
const { locationURL } = state['features/base/connection'];
|
||||
const { jwt } = state['features/base/jwt'];
|
||||
const { name: displayName } = getLocalParticipant(state);
|
||||
|
||||
// First create all promises for dialing out.
|
||||
const phoneNumbers
|
||||
@@ -168,21 +164,6 @@ export function invite(
|
||||
invitesLeftToSend
|
||||
= invitesLeftToSend.filter(({ type }) => type !== 'videosipgw');
|
||||
|
||||
const sipEndpoints
|
||||
= invitesLeftToSend.filter(({ type }) => type === 'sip');
|
||||
|
||||
conference && inviteSipEndpoints(
|
||||
sipEndpoints,
|
||||
locationURL,
|
||||
sipInviteUrl,
|
||||
jwt,
|
||||
conference.options.name,
|
||||
displayName
|
||||
);
|
||||
|
||||
invitesLeftToSend
|
||||
= invitesLeftToSend.filter(({ type }) => type !== 'sip');
|
||||
|
||||
return (
|
||||
Promise.all(allInvitePromises)
|
||||
.then(() => invitesLeftToSend));
|
||||
|
||||
@@ -12,8 +12,7 @@ import {
|
||||
getInviteResultsForQuery,
|
||||
getInviteTypeCounts,
|
||||
isAddPeopleEnabled,
|
||||
isDialOutEnabled,
|
||||
isSipInviteEnabled
|
||||
isDialOutEnabled
|
||||
} from '../../functions';
|
||||
import logger from '../../logger';
|
||||
|
||||
@@ -39,11 +38,6 @@ export type Props = {
|
||||
*/
|
||||
_dialOutEnabled: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not to allow sip invites.
|
||||
*/
|
||||
_sipInviteEnabled: boolean,
|
||||
|
||||
/**
|
||||
* The JWT token.
|
||||
*/
|
||||
@@ -102,7 +96,7 @@ export default class AbstractAddPeopleDialog<P: Props, S: State>
|
||||
|
||||
/**
|
||||
* Invite people and numbers to the conference. The logic works by inviting
|
||||
* numbers, people/rooms, sip endpoints and videosipgw in parallel. All invitees are
|
||||
* numbers, people/rooms, and videosipgw in parallel. All invitees are
|
||||
* stored in an array. As each invite succeeds, the invitee is removed
|
||||
* from the array. After all invites finish, close the modal if there are
|
||||
* no invites left to send. If any are left, that means an invite failed
|
||||
@@ -220,8 +214,7 @@ export default class AbstractAddPeopleDialog<P: Props, S: State>
|
||||
_dialOutEnabled: dialOutEnabled,
|
||||
_jwt: jwt,
|
||||
_peopleSearchQueryTypes: peopleSearchQueryTypes,
|
||||
_peopleSearchUrl: peopleSearchUrl,
|
||||
_sipInviteEnabled: sipInviteEnabled
|
||||
_peopleSearchUrl: peopleSearchUrl
|
||||
} = this.props;
|
||||
const options = {
|
||||
addPeopleEnabled,
|
||||
@@ -229,8 +222,7 @@ export default class AbstractAddPeopleDialog<P: Props, S: State>
|
||||
dialOutEnabled,
|
||||
jwt,
|
||||
peopleSearchQueryTypes,
|
||||
peopleSearchUrl,
|
||||
sipInviteEnabled
|
||||
peopleSearchUrl
|
||||
};
|
||||
|
||||
return getInviteResultsForQuery(query, options);
|
||||
@@ -267,7 +259,6 @@ export function _mapStateToProps(state: Object) {
|
||||
_dialOutEnabled: isDialOutEnabled(state),
|
||||
_jwt: state['features/base/jwt'].jwt,
|
||||
_peopleSearchQueryTypes: peopleSearchQueryTypes,
|
||||
_peopleSearchUrl: peopleSearchUrl,
|
||||
_sipInviteEnabled: isSipInviteEnabled(state)
|
||||
_peopleSearchUrl: peopleSearchUrl
|
||||
};
|
||||
}
|
||||
|
||||
@@ -285,7 +285,7 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
|
||||
*/
|
||||
_parseQueryResults(response = []) {
|
||||
const { t, _dialOutEnabled } = this.props;
|
||||
const users = response.filter(item => item.type !== 'phone' && item.type !== 'sip');
|
||||
const users = response.filter(item => item.type !== 'phone');
|
||||
const userDisplayItems = [];
|
||||
|
||||
for (const user of users) {
|
||||
@@ -348,25 +348,9 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
const sipAddresses = response.filter(item => item.type === 'sip');
|
||||
|
||||
const sipDisplayItems = sipAddresses.map(sip => {
|
||||
return {
|
||||
filterValues: [
|
||||
sip.address
|
||||
],
|
||||
content: t('addPeople.sip', { address: sip.address }),
|
||||
description: '',
|
||||
item: sip,
|
||||
value: sip.address
|
||||
};
|
||||
});
|
||||
|
||||
return [
|
||||
...userDisplayItems,
|
||||
...numberDisplayItems,
|
||||
...sipDisplayItems
|
||||
...numberDisplayItems
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -42,9 +42,3 @@ export const OUTGOING_CALL_RINGING_SOUND_ID = 'OUTGOING_CALL_RINGING_SOUND_ID';
|
||||
* @type {string}
|
||||
*/
|
||||
export const OUTGOING_CALL_START_SOUND_ID = 'OUTGOING_CALL_START_SOUND_ID';
|
||||
|
||||
/**
|
||||
* Regex for matching sip addresses.
|
||||
*/
|
||||
// eslint-disable-next-line max-len
|
||||
export const SIP_ADDRESS_REGEX = /^[a-zA-Z]+(?:([^\s>:@]+)(?::([^\s@>]+))?@)?([\w\-.]+)(?::(\d+))?((?:;[^\s=?>;]+(?:=[^\s?;]+)?)*)(?:\?(([^\s&=>]+=[^\s&=>]+)(&[^\s&=>]+=[^\s&=>]+)*))?$/;
|
||||
|
||||
@@ -10,7 +10,6 @@ import { toState } from '../base/redux';
|
||||
import { doGetJSON, parseURIString } from '../base/util';
|
||||
import { isVpaasMeeting } from '../billing-counter/functions';
|
||||
|
||||
import { SIP_ADDRESS_REGEX } from './constants';
|
||||
import logger from './logger';
|
||||
|
||||
declare var $: Function;
|
||||
@@ -123,11 +122,6 @@ export type GetInviteResultsOptions = {
|
||||
*/
|
||||
peopleSearchUrl: string,
|
||||
|
||||
/**
|
||||
* Whether or not to check sip invites.
|
||||
*/
|
||||
sipInviteEnabled: boolean,
|
||||
|
||||
/**
|
||||
* The jwt token to pass to the search service.
|
||||
*/
|
||||
@@ -155,7 +149,6 @@ export function getInviteResultsForQuery(
|
||||
dialOutEnabled,
|
||||
peopleSearchQueryTypes,
|
||||
peopleSearchUrl,
|
||||
sipInviteEnabled,
|
||||
jwt
|
||||
} = options;
|
||||
|
||||
@@ -240,13 +233,6 @@ export function getInviteResultsForQuery(
|
||||
});
|
||||
}
|
||||
|
||||
if (sipInviteEnabled && isASipAddress(text)) {
|
||||
results.push({
|
||||
type: 'sip',
|
||||
address: text
|
||||
});
|
||||
}
|
||||
|
||||
return results;
|
||||
});
|
||||
}
|
||||
@@ -388,21 +374,6 @@ export function isDialOutEnabled(state: Object): boolean {
|
||||
&& conference && conference.isSIPCallingSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if inviting sip endpoints is enabled or not.
|
||||
*
|
||||
* @param {Object} state - Current state.
|
||||
* @returns {boolean} Indication of whether dial out is currently enabled.
|
||||
*/
|
||||
export function isSipInviteEnabled(state: Object): boolean {
|
||||
const { sipInviteUrl } = state['features/base/config'];
|
||||
const { features = {} } = getLocalParticipant(state);
|
||||
|
||||
return state['features/base/jwt'].jwt
|
||||
&& Boolean(sipInviteUrl)
|
||||
&& String(features['sip-outbound-call']) === 'true';
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a string looks like it could be for a phone number.
|
||||
*
|
||||
@@ -421,16 +392,6 @@ function isMaybeAPhoneNumber(text: string): boolean {
|
||||
return Boolean(digits.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether a string matches a sip address format.
|
||||
*
|
||||
* @param {string} text - The text to check.
|
||||
* @returns {boolean} True if provided text matches a sip address format.
|
||||
*/
|
||||
function isASipAddress(text: string): boolean {
|
||||
return SIP_ADDRESS_REGEX.test(text);
|
||||
}
|
||||
|
||||
/**
|
||||
* RegExp to use to determine if some text might be a phone number.
|
||||
*
|
||||
@@ -803,55 +764,3 @@ export function isSharingEnabled(sharingFeature: string) {
|
||||
|| typeof interfaceConfig.SHARING_FEATURES === 'undefined'
|
||||
|| (interfaceConfig.SHARING_FEATURES.length && interfaceConfig.SHARING_FEATURES.indexOf(sharingFeature) > -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a post request to an invite service.
|
||||
*
|
||||
* @param {Array} inviteItems - The list of the "sip" type items to invite.
|
||||
* @param {URL} locationURL - The URL of the location.
|
||||
* @param {string} sipInviteUrl - The invite service that generates the invitation.
|
||||
* @param {string} jwt - The jwt token.
|
||||
* @param {string} roomName - The name to the conference.
|
||||
* @param {string} displayName - The user display name.
|
||||
* @returns {Promise} - The promise created by the request.
|
||||
*/
|
||||
export function inviteSipEndpoints( // eslint-disable-line max-params
|
||||
inviteItems: Array<Object>,
|
||||
locationURL: URL,
|
||||
sipInviteUrl: string,
|
||||
jwt: string,
|
||||
roomName: string,
|
||||
displayName: string
|
||||
): Promise<void> {
|
||||
if (inviteItems.length === 0) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
const baseUrl = Object.assign(new URL(locationURL.toString()), {
|
||||
pathname: locationURL.pathname.replace(`/${roomName}`, ''),
|
||||
search: ''
|
||||
});
|
||||
|
||||
return fetch(
|
||||
sipInviteUrl,
|
||||
{
|
||||
body: JSON.stringify({
|
||||
callParams: {
|
||||
callUrlInfo: {
|
||||
baseUrl,
|
||||
callName: roomName
|
||||
}
|
||||
},
|
||||
sipClientParams: {
|
||||
displayName,
|
||||
sipAddress: inviteItems.map(item => item.address)
|
||||
}
|
||||
}),
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': `Bearer ${jwt}`,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import React, { Component } from 'react';
|
||||
import { Watermarks } from '../../base/react';
|
||||
import { connect } from '../../base/redux';
|
||||
import { setColorAlpha } from '../../base/util';
|
||||
import { Subject } from '../../conference';
|
||||
import { fetchCustomBrandingData } from '../../dynamic-branding';
|
||||
import { Captions } from '../../subtitles/';
|
||||
|
||||
@@ -75,6 +76,7 @@ class LargeVideo extends Component<Props> {
|
||||
className = { className }
|
||||
id = 'largeVideoContainer'
|
||||
style = { style }>
|
||||
<Subject />
|
||||
<div id = 'sharedVideo'>
|
||||
<div id = 'sharedVideoIFrame' />
|
||||
</div>
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import { type Dispatch } from 'redux';
|
||||
|
||||
import {
|
||||
getCurrentConference
|
||||
} from '../base/conference';
|
||||
|
||||
/**
|
||||
* Action to toggle lobby mode on or off.
|
||||
*
|
||||
* @param {boolean} enabled - The desired (new) state of the lobby mode.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function toggleLobbyMode(enabled: boolean) {
|
||||
return async (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const conference = getCurrentConference(getState);
|
||||
|
||||
if (enabled) {
|
||||
conference.enableLobby();
|
||||
} else {
|
||||
conference.disableLobby();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
} from '../base/conference';
|
||||
import { hideDialog, openDialog } from '../base/dialog';
|
||||
import { getLocalParticipant } from '../base/participants';
|
||||
export * from './actions.any';
|
||||
|
||||
import {
|
||||
KNOCKING_PARTICIPANT_ARRIVED_OR_UPDATED,
|
||||
@@ -199,3 +198,20 @@ export function startKnocking() {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to toggle lobby mode on or off.
|
||||
*
|
||||
* @param {boolean} enabled - The desired (new) state of the lobby mode.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function toggleLobbyMode(enabled: boolean) {
|
||||
return async (dispatch: Dispatch<any>, getState: Function) => {
|
||||
const conference = getCurrentConference(getState);
|
||||
|
||||
if (enabled) {
|
||||
conference.enableLobby();
|
||||
} else {
|
||||
conference.disableLobby();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
78
react/features/lobby/components/native/LobbyModeButton.js
Normal file
78
react/features/lobby/components/native/LobbyModeButton.js
Normal file
@@ -0,0 +1,78 @@
|
||||
// @flow
|
||||
|
||||
import { getCurrentConference } from '../../../base/conference';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { IconMeetingUnlocked, IconMeetingLocked } from '../../../base/icons';
|
||||
import { isLocalParticipantModerator } from '../../../base/participants';
|
||||
import { connect } from '../../../base/redux';
|
||||
import AbstractButton, { type Props as AbstractProps } from '../../../base/toolbox/components/AbstractButton';
|
||||
import { showDisableLobbyModeDialog, showEnableLobbyModeDialog } from '../../actions.native';
|
||||
|
||||
type Props = AbstractProps & {
|
||||
|
||||
/**
|
||||
* The Redux Dispatch function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
|
||||
/**
|
||||
* True if the lobby mode is currently enabled for this conference.
|
||||
*/
|
||||
lobbyEnabled: boolean
|
||||
};
|
||||
|
||||
/**
|
||||
* Component to render the lobby mode initiator button.
|
||||
*/
|
||||
class LobbyModeButton extends AbstractButton<Props, any> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.lobbyButton';
|
||||
icon = IconMeetingUnlocked;
|
||||
label = 'toolbar.lobbyButtonEnable';
|
||||
toggledLabel = 'toolbar.lobbyButtonDisable'
|
||||
toggledIcon = IconMeetingLocked;
|
||||
|
||||
/**
|
||||
* Callback for the click event of the button.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
if (this._isToggled()) {
|
||||
dispatch(showDisableLobbyModeDialog());
|
||||
} else {
|
||||
dispatch(showEnableLobbyModeDialog());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to define the button state.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_isToggled() {
|
||||
return this.props.lobbyEnabled;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps part of the Redux store to the props of this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @param {Props} ownProps - The own props of the component.
|
||||
* @returns {Props}
|
||||
*/
|
||||
export function _mapStateToProps(state: Object): $Shape<Props> {
|
||||
const conference = getCurrentConference(state);
|
||||
const { lobbyEnabled } = state['features/lobby'];
|
||||
const { hideLobbyButton } = state['features/base/config'];
|
||||
const lobbySupported = conference && conference.isLobbySupported();
|
||||
|
||||
return {
|
||||
lobbyEnabled,
|
||||
visible: lobbySupported && isLocalParticipantModerator(state) && !hideLobbyButton
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(LobbyModeButton));
|
||||
@@ -1,58 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { Switch, View } from 'react-native';
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { connect } from '../../../base/redux';
|
||||
|
||||
import styles, {
|
||||
ENABLED_THUMB_COLOR,
|
||||
ENABLED_TRACK_COLOR,
|
||||
DISABLED_THUMB_COLOR
|
||||
} from './styles';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link LobbyModeSwitch}.
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* True if the lobby mode is currently enabled for this conference.
|
||||
*/
|
||||
lobbyEnabled: boolean,
|
||||
|
||||
/**
|
||||
* Callback to be invoked when handling enable-disable lobby mode switch.
|
||||
*/
|
||||
onToggleLobbyMode: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* Component meant to Enable/Disable lobby mode.
|
||||
*
|
||||
* @returns {React$Element<any>}
|
||||
*/
|
||||
function LobbyModeSwitch(
|
||||
{
|
||||
lobbyEnabled,
|
||||
onToggleLobbyMode
|
||||
}: Props) {
|
||||
|
||||
return (
|
||||
<View style = { styles.lobbySwitchContainer }>
|
||||
<Switch
|
||||
onValueChange = { onToggleLobbyMode }
|
||||
style = { styles.lobbySwitchIcon }
|
||||
thumbColor = {
|
||||
lobbyEnabled
|
||||
? ENABLED_THUMB_COLOR
|
||||
: DISABLED_THUMB_COLOR
|
||||
}
|
||||
trackColor = {{ true: ENABLED_TRACK_COLOR }}
|
||||
value = { lobbyEnabled } />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export default translate(connect()(LobbyModeSwitch));
|
||||
@@ -3,4 +3,5 @@
|
||||
export { default as DisableLobbyModeDialog } from './DisableLobbyModeDialog';
|
||||
export { default as EnableLobbyModeDialog } from './EnableLobbyModeDialog';
|
||||
export { default as KnockingParticipantList } from './KnockingParticipantList';
|
||||
export { default as LobbyModeButton } from './LobbyModeButton';
|
||||
export { default as LobbyScreen } from './LobbyScreen';
|
||||
|
||||
@@ -4,10 +4,6 @@ import { ColorPalette } from '../../../base/styles';
|
||||
|
||||
const SECONDARY_COLOR = '#B8C7E0';
|
||||
|
||||
export const ENABLED_THUMB_COLOR = ColorPalette.blueHighlight;
|
||||
export const ENABLED_TRACK_COLOR = ColorPalette.blue;
|
||||
export const DISABLED_THUMB_COLOR = ColorPalette.darkGrey;
|
||||
|
||||
export default {
|
||||
button: {
|
||||
alignItems: 'center',
|
||||
@@ -142,14 +138,5 @@ export default {
|
||||
|
||||
knockingParticipantListText: {
|
||||
color: 'white'
|
||||
},
|
||||
|
||||
lobbySwitchContainer: {
|
||||
flexDirection: 'column',
|
||||
marginTop: 16
|
||||
},
|
||||
|
||||
lobbySwitchIcon: {
|
||||
alignSelf: 'flex-end'
|
||||
}
|
||||
};
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { translate } from '../../base/i18n/index';
|
||||
import { Label } from '../../base/label/index';
|
||||
import { CircularLabel } from '../../base/label/index';
|
||||
import { connect } from '../../base/redux';
|
||||
import { Tooltip } from '../../base/tooltip';
|
||||
|
||||
@@ -45,10 +45,10 @@ class LocalRecordingLabel extends Component<Props> {
|
||||
return (
|
||||
<Tooltip
|
||||
content = { this.props.t('localRecording.labelToolTip') }
|
||||
position = { 'bottom' }>
|
||||
<Label
|
||||
position = { 'left' }>
|
||||
<CircularLabel
|
||||
className = 'local-rec'
|
||||
text = { this.props.t('localRecording.label') } />
|
||||
label = { this.props.t('localRecording.label') } />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -75,8 +75,7 @@ function _onImmersiveChange({ getState }) {
|
||||
const { enabled: audioOnly } = state['features/base/audio-only'];
|
||||
const conference = getCurrentConference(state);
|
||||
const dialogOpen = isAnyDialogOpen(state);
|
||||
const fullscreenEnabled = getFeatureFlag(state, FULLSCREEN_ENABLED, true);
|
||||
const fullScreen = conference ? !audioOnly && !dialogOpen && fullscreenEnabled : false;
|
||||
const fullScreen = conference ? !audioOnly && !dialogOpen : false;
|
||||
|
||||
_setFullScreen(fullScreen);
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { Label } from '../../../base/label';
|
||||
import { CircularLabel } from '../../../base/label';
|
||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||
import { connect } from '../../../base/redux';
|
||||
import AbstractRecordingLabel, {
|
||||
@@ -52,10 +52,10 @@ class RecordingLabel extends AbstractRecordingLabel {
|
||||
}
|
||||
|
||||
return (
|
||||
<Label
|
||||
<CircularLabel
|
||||
label = { this.props.t(this._getLabelKey()) }
|
||||
status = { status }
|
||||
style = { indicatorStyle }
|
||||
text = { this.props.t(this._getLabelKey()) } />
|
||||
style = { indicatorStyle } />
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { Label } from '../../../base/label';
|
||||
import { CircularLabel } from '../../../base/label';
|
||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||
import { connect } from '../../../base/redux';
|
||||
import AbstractRecordingLabel, {
|
||||
@@ -31,9 +31,9 @@ class RecordingLabel extends AbstractRecordingLabel {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Label
|
||||
<CircularLabel
|
||||
className = { this.props.mode }
|
||||
text = { this.props.t(this._getLabelKey()) } />
|
||||
label = { this.props.t(this._getLabelKey()) } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,12 +12,34 @@ import {
|
||||
setPassword
|
||||
} from '../base/conference';
|
||||
import { hideDialog, openDialog } from '../base/dialog';
|
||||
import { SecurityDialog } from '../security/components/security-dialog';
|
||||
|
||||
import { PasswordRequiredPrompt } from './components';
|
||||
import { PasswordRequiredPrompt, RoomLockPrompt } from './components';
|
||||
|
||||
declare var APP: Object;
|
||||
|
||||
/**
|
||||
* Begins a (user) request to lock a specific conference/room.
|
||||
*
|
||||
* @param {JitsiConference|undefined} conference - The JitsiConference to lock
|
||||
* if specified or undefined if the current JitsiConference is to be locked.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function beginRoomLockRequest(conference: ?Object) {
|
||||
return (dispatch: Function, getState: Function) => {
|
||||
if (typeof conference === 'undefined') {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
conference = getState()['features/base/conference'].conference;
|
||||
}
|
||||
if (conference) {
|
||||
const passwordNumberOfDigits = getState()['features/base/config'].roomPasswordNumberOfDigits;
|
||||
|
||||
dispatch(openDialog(RoomLockPrompt, {
|
||||
conference,
|
||||
passwordNumberOfDigits }));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancels a prompt for a password to join a specific conference/room.
|
||||
*
|
||||
@@ -77,7 +99,7 @@ export function endRoomLockRequest(
|
||||
= password
|
||||
? dispatch(setPassword(conference, conference.lock, password))
|
||||
: Promise.resolve();
|
||||
const endRoomLockRequest_ = () => dispatch(hideDialog(SecurityDialog));
|
||||
const endRoomLockRequest_ = () => dispatch(hideDialog(RoomLockPrompt));
|
||||
|
||||
setPassword_.then(endRoomLockRequest_, endRoomLockRequest_);
|
||||
};
|
||||
@@ -115,5 +137,3 @@ export function unlockRoom() {
|
||||
));
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
101
react/features/room-lock/components/RoomLockButton.js
Normal file
101
react/features/room-lock/components/RoomLockButton.js
Normal file
@@ -0,0 +1,101 @@
|
||||
// @flow
|
||||
|
||||
import { MEETING_PASSWORD_ENABLED, getFeatureFlag } from '../../base/flags';
|
||||
import { translate } from '../../base/i18n';
|
||||
import { IconRoomLock, IconRoomUnlock } from '../../base/icons';
|
||||
import { isLocalParticipantModerator } from '../../base/participants';
|
||||
import { connect } from '../../base/redux';
|
||||
import { AbstractButton, type AbstractButtonProps } from '../../base/toolbox/components';
|
||||
import { beginRoomLockRequest, unlockRoom } from '../actions';
|
||||
|
||||
type Props = AbstractButtonProps & {
|
||||
|
||||
/**
|
||||
* Whether the current local participant is a moderator, therefore is
|
||||
* allowed to lock or unlock the conference.
|
||||
*/
|
||||
_localParticipantModerator: boolean,
|
||||
|
||||
/**
|
||||
* Whether the current conference is locked or not.
|
||||
*/
|
||||
_locked: boolean,
|
||||
|
||||
/**
|
||||
* The redux {@code dispatch} function.
|
||||
*/
|
||||
dispatch: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* An implementation of a button for locking / unlocking a room.
|
||||
*/
|
||||
class RoomLockButton extends AbstractButton<Props, *> {
|
||||
accessibilityLabel = 'toolbar.accessibilityLabel.lockRoom';
|
||||
icon = IconRoomLock;
|
||||
label = 'dialog.lockRoom';
|
||||
toggledIcon = IconRoomUnlock;
|
||||
toggledLabel = 'dialog.unlockRoom';
|
||||
|
||||
/**
|
||||
* Handles clicking / pressing the button.
|
||||
*
|
||||
* @override
|
||||
* @protected
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
const { dispatch, _locked } = this.props;
|
||||
|
||||
if (_locked) {
|
||||
dispatch(unlockRoom());
|
||||
} else {
|
||||
dispatch(beginRoomLockRequest());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this button is disabled or not.
|
||||
*
|
||||
* @override
|
||||
* @protected
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_isDisabled() {
|
||||
return !this.props._localParticipantModerator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this button is in toggled state or not.
|
||||
*
|
||||
* @override
|
||||
* @protected
|
||||
* @returns {boolean}
|
||||
*/
|
||||
_isToggled() {
|
||||
return this.props._locked;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps (parts of) the redux state to the associated props for the
|
||||
* {@code RoomLockButton} component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @param {Object} ownProps - The properties explicitly passed to the component instance.
|
||||
* @private
|
||||
* @returns {Props}
|
||||
*/
|
||||
function _mapStateToProps(state, ownProps): Object {
|
||||
const { conference, locked } = state['features/base/conference'];
|
||||
const enabled = getFeatureFlag(state, MEETING_PASSWORD_ENABLED, true);
|
||||
const { visible = enabled } = ownProps;
|
||||
|
||||
return {
|
||||
_localParticipantModerator: Boolean(conference && isLocalParticipantModerator(state)),
|
||||
_locked: Boolean(conference && locked),
|
||||
visible
|
||||
};
|
||||
}
|
||||
|
||||
export default translate(connect(_mapStateToProps)(RoomLockButton));
|
||||
140
react/features/room-lock/components/RoomLockPrompt.native.js
Normal file
140
react/features/room-lock/components/RoomLockPrompt.native.js
Normal file
@@ -0,0 +1,140 @@
|
||||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { InputDialog } from '../../base/dialog';
|
||||
import { connect } from '../../base/redux';
|
||||
import { endRoomLockRequest } from '../actions';
|
||||
|
||||
/**
|
||||
* The style of the {@link TextInput} rendered by {@code RoomLockPrompt}. As it
|
||||
* requests the entry of a password, {@code TextInput} automatically correcting
|
||||
* the entry of the password is a pain to deal with as a user.
|
||||
*/
|
||||
const _TEXT_INPUT_PROPS = {
|
||||
autoCapitalize: 'none',
|
||||
autoCorrect: false
|
||||
};
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link RoomLockPrompt}.
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The JitsiConference which requires a password.
|
||||
*/
|
||||
conference: Object,
|
||||
|
||||
/**
|
||||
* The number of digits to be used in the password.
|
||||
*/
|
||||
passwordNumberOfDigits: ?number,
|
||||
|
||||
/**
|
||||
* Redux store dispatch function.
|
||||
*/
|
||||
dispatch: Dispatch<any>
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements a React Component which prompts the user for a password to lock a
|
||||
* conference/room.
|
||||
*/
|
||||
class RoomLockPrompt extends Component<Props> {
|
||||
/**
|
||||
* Initializes a new RoomLockPrompt instance.
|
||||
*
|
||||
* @param {Props} props - The read-only properties with which the new
|
||||
* instance is to be initialized.
|
||||
*/
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
// Bind event handlers so they are only bound once per instance.
|
||||
this._onCancel = this._onCancel.bind(this);
|
||||
this._onSubmit = this._onSubmit.bind(this);
|
||||
this._validateInput = this._validateInput.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
let textInputProps = _TEXT_INPUT_PROPS;
|
||||
|
||||
if (this.props.passwordNumberOfDigits) {
|
||||
textInputProps = {
|
||||
...textInputProps,
|
||||
keyboardType: 'number-pad',
|
||||
maxLength: this.props.passwordNumberOfDigits
|
||||
};
|
||||
}
|
||||
|
||||
return (
|
||||
<InputDialog
|
||||
contentKey = 'security.about'
|
||||
onCancel = { this._onCancel }
|
||||
onSubmit = { this._onSubmit }
|
||||
textInputProps = { textInputProps }
|
||||
validateInput = { this._validateInput } />
|
||||
);
|
||||
}
|
||||
|
||||
_onCancel: () => boolean;
|
||||
|
||||
/**
|
||||
* Notifies this prompt that it has been dismissed by cancel.
|
||||
*
|
||||
* @private
|
||||
* @returns {boolean} True to hide this dialog/prompt; otherwise, false.
|
||||
*/
|
||||
_onCancel() {
|
||||
// An undefined password is understood to cancel the request to lock the
|
||||
// conference/room.
|
||||
return this._onSubmit(undefined);
|
||||
}
|
||||
|
||||
_onSubmit: (?string) => boolean;
|
||||
|
||||
/**
|
||||
* Notifies this prompt that it has been dismissed by submitting a specific
|
||||
* value.
|
||||
*
|
||||
* @param {string|undefined} value - The submitted value.
|
||||
* @private
|
||||
* @returns {boolean} False because we do not want to hide this
|
||||
* dialog/prompt as the hiding will be handled inside endRoomLockRequest
|
||||
* after setting the password is resolved.
|
||||
*/
|
||||
_onSubmit(value: ?string) {
|
||||
this.props.dispatch(endRoomLockRequest(this.props.conference, value));
|
||||
|
||||
return false; // Do not hide.
|
||||
}
|
||||
|
||||
_validateInput: (string) => boolean;
|
||||
|
||||
/**
|
||||
* Verifies input in case only digits are required.
|
||||
*
|
||||
* @param {string|undefined} value - The submitted value.
|
||||
* @private
|
||||
* @returns {boolean} False when the value is not valid and True otherwise.
|
||||
*/
|
||||
_validateInput(value: string) {
|
||||
|
||||
// we want only digits, but both number-pad and numeric add ',' and '.' as symbols
|
||||
if (this.props.passwordNumberOfDigits && value.length > 0 && !/^\d+$/.test(value)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
export default connect()(RoomLockPrompt);
|
||||
@@ -1,83 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import React from 'react';
|
||||
import { Switch, Text, View } from 'react-native';
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
import { connect } from '../../base/redux';
|
||||
import { LOCKED_REMOTELY } from '../constants';
|
||||
|
||||
import styles, {
|
||||
DISABLED_THUMB_COLOR,
|
||||
ENABLED_THUMB_COLOR, ENABLED_TRACK_COLOR
|
||||
} from './styles';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link RoomLockSwitch}.
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Checks if the room is locked based on defined room lock constants.
|
||||
*/
|
||||
locked: string,
|
||||
|
||||
/**
|
||||
* Whether the switch is disabled.
|
||||
*/
|
||||
disabled: boolean,
|
||||
|
||||
/**
|
||||
* Callback to be invoked when the user toggles room lock.
|
||||
*/
|
||||
onToggleRoomLock: Function,
|
||||
|
||||
/**
|
||||
* Control for room lock.
|
||||
*/
|
||||
toggleRoomLock: boolean,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* Component meant to Add/Remove meeting password.
|
||||
*
|
||||
* @returns {React$Element<any>}
|
||||
*/
|
||||
function RoomLockSwitch(
|
||||
{
|
||||
locked,
|
||||
disabled,
|
||||
onToggleRoomLock,
|
||||
toggleRoomLock,
|
||||
t
|
||||
}: Props) {
|
||||
|
||||
return (
|
||||
<View style = { styles.roomLockSwitchContainer }>
|
||||
<Text>
|
||||
{
|
||||
locked === LOCKED_REMOTELY
|
||||
&& t('passwordSetRemotely')
|
||||
}
|
||||
</Text>
|
||||
<Switch
|
||||
disabled = { disabled }
|
||||
onValueChange = { onToggleRoomLock }
|
||||
thumbColor = {
|
||||
toggleRoomLock
|
||||
? ENABLED_THUMB_COLOR
|
||||
: DISABLED_THUMB_COLOR
|
||||
}
|
||||
trackColor = {{ true: ENABLED_TRACK_COLOR }}
|
||||
value = { toggleRoomLock } />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export default translate(connect()(RoomLockSwitch));
|
||||
@@ -1 +1,3 @@
|
||||
export { default as PasswordRequiredPrompt } from './PasswordRequiredPrompt';
|
||||
export { default as RoomLockButton } from './RoomLockButton';
|
||||
export { default as RoomLockPrompt } from './RoomLockPrompt';
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import { ColorPalette } from '../../base/styles';
|
||||
|
||||
export const ENABLED_THUMB_COLOR = ColorPalette.blueHighlight;
|
||||
export const ENABLED_TRACK_COLOR = ColorPalette.blue;
|
||||
export const DISABLED_THUMB_COLOR = ColorPalette.darkGrey;
|
||||
|
||||
export default {
|
||||
roomLockSwitchContainer: {
|
||||
alignItems: 'center',
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
marginTop: 16
|
||||
}
|
||||
};
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
} from '../notifications';
|
||||
|
||||
import { _openPasswordRequiredPrompt } from './actions';
|
||||
import { PasswordRequiredPrompt } from './components';
|
||||
import { PasswordRequiredPrompt, RoomLockPrompt } from './components';
|
||||
import { LOCKED_REMOTELY } from './constants';
|
||||
import logger from './logger';
|
||||
|
||||
@@ -85,6 +85,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
*/
|
||||
function _conferenceJoined({ dispatch }, next, action) {
|
||||
dispatch(hideDialog(PasswordRequiredPrompt));
|
||||
dispatch(hideDialog(RoomLockPrompt));
|
||||
|
||||
return next(action);
|
||||
}
|
||||
@@ -115,6 +116,7 @@ function _conferenceFailed({ dispatch }, next, action) {
|
||||
}
|
||||
} else {
|
||||
dispatch(hideDialog(PasswordRequiredPrompt));
|
||||
dispatch(hideDialog(RoomLockPrompt));
|
||||
}
|
||||
|
||||
return next(action);
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
// @flow
|
||||
|
||||
/**
|
||||
* Type of action which sets the current state of screen audio sharing.
|
||||
*
|
||||
* {
|
||||
* type: SET_SCREEN_AUDIO_SHARE_STATE,
|
||||
* isSharingAudio: boolean
|
||||
* }
|
||||
*/
|
||||
export const SET_SCREEN_AUDIO_SHARE_STATE = 'SET_SCREEN_AUDIO_SHARE_STATE';
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import { SET_SCREEN_AUDIO_SHARE_STATE } from './actionTypes';
|
||||
|
||||
/**
|
||||
* Updates the current known status of the shared video.
|
||||
*
|
||||
* @param {boolean} isSharingAudio - Is audio currently being shared or not.
|
||||
* @returns {{
|
||||
* type: SET_SCREEN_AUDIO_SHARE_STATE,
|
||||
* isSharingAudio: boolean
|
||||
* }}
|
||||
*/
|
||||
export function setScreenAudioShareState(isSharingAudio: boolean) {
|
||||
return {
|
||||
type: SET_SCREEN_AUDIO_SHARE_STATE,
|
||||
isSharingAudio
|
||||
};
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import { isMacOS } from '../base/environment';
|
||||
import { browser } from '../base/lib-jitsi-meet';
|
||||
|
||||
|
||||
/**
|
||||
* State of audio sharing.
|
||||
*
|
||||
* @param {Object} state - The state of the application.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isScreenAudioShared(state: Object) {
|
||||
return state['features/screen-share'].isSharingAudio;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the visibility of the audio only screen share button. Currently electron on mac os doesn't
|
||||
* have support for this functionality.
|
||||
*
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isScreenAudioSupported() {
|
||||
return !(browser.isElectron() && isMacOS());
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
export * from './actions';
|
||||
export * from './actionTypes';
|
||||
export * from './functions';
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
|
||||
import { ReducerRegistry } from '../base/redux';
|
||||
|
||||
import { SET_SCREEN_AUDIO_SHARE_STATE } from './actionTypes';
|
||||
|
||||
/**
|
||||
* Reduces the Redux actions of the feature features/screen-share.
|
||||
*/
|
||||
ReducerRegistry.register('features/screen-share', (state = {}, action) => {
|
||||
const { isSharingAudio } = action;
|
||||
|
||||
switch (action.type) {
|
||||
case SET_SCREEN_AUDIO_SHARE_STATE:
|
||||
return {
|
||||
...state,
|
||||
isSharingAudio
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
});
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user