mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-03-12 22:50:20 +00:00
Compare commits
138 Commits
5128
...
enable-deb
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1433a1ee5d | ||
|
|
834ee22bc3 | ||
|
|
d6b5687828 | ||
|
|
6b496d4def | ||
|
|
22cc56ce8d | ||
|
|
0419c5a15b | ||
|
|
dda1f3c5ba | ||
|
|
6f41ef75d7 | ||
|
|
46cbc0ff49 | ||
|
|
53a695da90 | ||
|
|
8bbee7d1dc | ||
|
|
72d4aa7dd5 | ||
|
|
8161309e28 | ||
|
|
465e7f1458 | ||
|
|
4e43a31ec9 | ||
|
|
70c5ea04b1 | ||
|
|
ca25be7314 | ||
|
|
3c2ad24652 | ||
|
|
e421a119e1 | ||
|
|
619acaca24 | ||
|
|
bc9f5773fb | ||
|
|
d0be8dcf9d | ||
|
|
af9958ad66 | ||
|
|
efc5c9dabe | ||
|
|
d22fc88ae3 | ||
|
|
9ee75038b6 | ||
|
|
09af88088d | ||
|
|
2e539ba010 | ||
|
|
87b3ec2cc0 | ||
|
|
907b51925d | ||
|
|
643340c4a6 | ||
|
|
d6c821d524 | ||
|
|
eb16f93153 | ||
|
|
47576aebba | ||
|
|
bac0a55421 | ||
|
|
1c8103c444 | ||
|
|
4e83e93eb6 | ||
|
|
0f8fa4f059 | ||
|
|
becaf0806a | ||
|
|
5b77d722d7 | ||
|
|
f4cde2192e | ||
|
|
e91df47d1b | ||
|
|
2d04f3852c | ||
|
|
2209394d09 | ||
|
|
1e76dc0aa2 | ||
|
|
75edfc1fab | ||
|
|
8c20dd8e47 | ||
|
|
fefe451180 | ||
|
|
b268e01a42 | ||
|
|
d62e378528 | ||
|
|
e8ad2365b6 | ||
|
|
b7389e1c31 | ||
|
|
eeddf6b350 | ||
|
|
665b7730ee | ||
|
|
7854437e31 | ||
|
|
600af62945 | ||
|
|
88ddb8d9b4 | ||
|
|
5182a720f9 | ||
|
|
415562c315 | ||
|
|
53d0a892b5 | ||
|
|
9b220f3870 | ||
|
|
c6e50ad439 | ||
|
|
36cb896680 | ||
|
|
249515ac60 | ||
|
|
80b49266ab | ||
|
|
1afae50923 | ||
|
|
b332fb474b | ||
|
|
a12ad99ecf | ||
|
|
400f47963d | ||
|
|
65fbc6f256 | ||
|
|
e7a324185f | ||
|
|
14a5c45fa3 | ||
|
|
05e6dde341 | ||
|
|
e13473d42f | ||
|
|
4b72fefd7e | ||
|
|
ba9398a1e2 | ||
|
|
8d4cf7165e | ||
|
|
0b3991d9e1 | ||
|
|
47be509d17 | ||
|
|
ba64d3e0c8 | ||
|
|
cd05c34d19 | ||
|
|
24550777c6 | ||
|
|
ee101f8947 | ||
|
|
8ca85f9e1c | ||
|
|
34ccd56691 | ||
|
|
f49c05c666 | ||
|
|
e7280e5040 | ||
|
|
eb1add681f | ||
|
|
8419dc725c | ||
|
|
f984faef3f | ||
|
|
0c76d7532c | ||
|
|
cb0b68f840 | ||
|
|
08a4da22f3 | ||
|
|
bdd6638067 | ||
|
|
8b44e06f2c | ||
|
|
79edc1b358 | ||
|
|
6597bfc2aa | ||
|
|
e0a2320d75 | ||
|
|
81e9fca03b | ||
|
|
76f8302aeb | ||
|
|
7263829763 | ||
|
|
b7ab3ea052 | ||
|
|
c657f360e1 | ||
|
|
ae5edf5a62 | ||
|
|
2bac757ca6 | ||
|
|
c10805f81b | ||
|
|
251eec19cd | ||
|
|
4276f82c03 | ||
|
|
4c3aae1e28 | ||
|
|
12be14bd4b | ||
|
|
420a7d8110 | ||
|
|
17f77a4246 | ||
|
|
6f9944a2d0 | ||
|
|
73328810e4 | ||
|
|
bb8c30a6c9 | ||
|
|
c5438ecd0c | ||
|
|
e22a25b216 | ||
|
|
4075e5deb7 | ||
|
|
ea0d953d1c | ||
|
|
b3e03fe50c | ||
|
|
8f81a75a61 | ||
|
|
0ab905bf75 | ||
|
|
5a3607f63f | ||
|
|
d57e202d19 | ||
|
|
1223c63f69 | ||
|
|
25b4887f74 | ||
|
|
e8ee65db82 | ||
|
|
9956ca6551 | ||
|
|
03a96d0be2 | ||
|
|
3fa7c01f19 | ||
|
|
5cba6c7bc7 | ||
|
|
52ee9b5151 | ||
|
|
d5f379a97c | ||
|
|
84cdd31731 | ||
|
|
b7eba915af | ||
|
|
7d0722c031 | ||
|
|
7d3953de51 | ||
|
|
601ee219e7 |
@@ -25,5 +25,5 @@ android.enableDexingArtifactTransform.desugaring=false
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
|
||||
appVersion=21.2.0
|
||||
sdkVersion=3.6.0
|
||||
appVersion=21.3.0
|
||||
sdkVersion=3.8.0
|
||||
|
||||
@@ -70,11 +70,13 @@ dependencies {
|
||||
implementation project(':react-native-default-preference')
|
||||
implementation project(':react-native-immersive')
|
||||
implementation project(':react-native-keep-awake')
|
||||
implementation project(':react-native-slider')
|
||||
implementation project(':react-native-sound')
|
||||
implementation project(':react-native-splash-screen')
|
||||
implementation project(':react-native-svg')
|
||||
implementation project(':react-native-video')
|
||||
implementation project(':react-native-webrtc')
|
||||
implementation project(':react-native-webview')
|
||||
implementation project(':react-native-splash-screen')
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
}
|
||||
|
||||
@@ -40,7 +40,8 @@
|
||||
|
||||
<service
|
||||
android:name=".ConnectionService"
|
||||
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
|
||||
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
|
||||
android:exported="true">
|
||||
<intent-filter>
|
||||
<action android:name="android.telecom.ConnectionService" />
|
||||
</intent-filter>
|
||||
|
||||
@@ -187,9 +187,11 @@ class ReactInstanceManagerHolder {
|
||||
new com.ocetnik.timer.BackgroundTimerPackage(),
|
||||
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
|
||||
new com.reactnativecommunity.netinfo.NetInfoPackage(),
|
||||
new com.reactnativecommunity.slider.ReactSliderPackage(),
|
||||
new com.reactnativecommunity.webview.RNCWebViewPackage(),
|
||||
new com.rnimmersive.RNImmersivePackage(),
|
||||
new com.zmxv.RNSound.RNSoundPackage(),
|
||||
new com.brentvatne.react.ReactVideoPackage(),
|
||||
new ReactPackageAdapter() {
|
||||
@Override
|
||||
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
|
||||
@@ -19,13 +19,17 @@ include ':react-native-immersive'
|
||||
project(':react-native-immersive').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-immersive/android')
|
||||
include ':react-native-keep-awake'
|
||||
project(':react-native-keep-awake').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-keep-awake/android')
|
||||
include ':react-native-slider'
|
||||
project(':react-native-slider').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/slider/android')
|
||||
include ':react-native-sound'
|
||||
project(':react-native-sound').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-sound/android')
|
||||
include ':react-native-splash-screen'
|
||||
project(':react-native-splash-screen').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-splash-screen/android')
|
||||
include ':react-native-svg'
|
||||
project(':react-native-svg').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-svg/android')
|
||||
include ':react-native-video'
|
||||
project(':react-native-video').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-video/android')
|
||||
include ':react-native-webrtc'
|
||||
project(':react-native-webrtc').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webrtc/android')
|
||||
include ':react-native-webview'
|
||||
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')
|
||||
project(':react-native-webview').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-webview/android')
|
||||
|
||||
@@ -39,6 +39,7 @@ import {
|
||||
conferenceWillJoin,
|
||||
conferenceWillLeave,
|
||||
dataChannelOpened,
|
||||
getConferenceOptions,
|
||||
kickedOut,
|
||||
lockStateChanged,
|
||||
onStartMutedPolicyChanged,
|
||||
@@ -111,7 +112,6 @@ import {
|
||||
trackRemoved
|
||||
} from './react/features/base/tracks';
|
||||
import { downloadJSON } from './react/features/base/util/downloadJSON';
|
||||
import { getConferenceOptions } from './react/features/conference/functions';
|
||||
import { showDesktopPicker } from './react/features/desktop-picker';
|
||||
import { appendSuffix } from './react/features/display-name';
|
||||
import {
|
||||
@@ -132,6 +132,7 @@ import { setScreenAudioShareState, isScreenAudioShared } from './react/features/
|
||||
import { toggleScreenshotCaptureEffect } from './react/features/screenshot-capture';
|
||||
import { AudioMixerEffect } from './react/features/stream-effects/audio-mixer/AudioMixerEffect';
|
||||
import { createPresenterEffect } from './react/features/stream-effects/presenter';
|
||||
import { createRnnoiseProcessor } from './react/features/stream-effects/rnnoise';
|
||||
import { endpointMessageReceived } from './react/features/subtitles';
|
||||
import UIEvents from './service/UI/UIEvents';
|
||||
|
||||
@@ -1357,7 +1358,11 @@ export default {
|
||||
},
|
||||
|
||||
_getConferenceOptions() {
|
||||
return getConferenceOptions(APP.store.getState());
|
||||
const options = getConferenceOptions(APP.store.getState());
|
||||
|
||||
options.createVADProcessor = createRnnoiseProcessor;
|
||||
|
||||
return options;
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
46
config.js
46
config.js
@@ -70,6 +70,9 @@ var config = {
|
||||
// callStatsThreshold: 5 // enable callstats for 5% of the users.
|
||||
},
|
||||
|
||||
// Enables reactions feature.
|
||||
// enableReactions: false,
|
||||
|
||||
// Disables ICE/UDP by filtering out local and remote UDP candidates in
|
||||
// signalling.
|
||||
// webrtcIceUdpDisable: false,
|
||||
@@ -459,11 +462,38 @@ var config = {
|
||||
// - 'desktop' controls the "Share your screen" button
|
||||
// - if `toolbarButtons` is undefined, we fallback to enabling all buttons on the UI
|
||||
// toolbarButtons: [
|
||||
// 'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',
|
||||
// 'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
|
||||
// 'livestreaming', 'etherpad', 'sharedvideo', 'shareaudio', 'settings', 'raisehand',
|
||||
// 'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
|
||||
// 'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone', 'security'
|
||||
// 'camera',
|
||||
// 'chat',
|
||||
// 'closedcaptions',
|
||||
// 'desktop',
|
||||
// 'download',
|
||||
// 'embedmeeting',
|
||||
// 'etherpad',
|
||||
// 'feedback',
|
||||
// 'filmstrip',
|
||||
// 'fullscreen',
|
||||
// 'hangup',
|
||||
// 'help',
|
||||
// 'invite',
|
||||
// 'livestreaming',
|
||||
// 'microphone',
|
||||
// 'mute-everyone',
|
||||
// 'mute-video-everyone',
|
||||
// 'participants-pane',
|
||||
// 'profile',
|
||||
// 'raisehand',
|
||||
// 'recording',
|
||||
// 'security',
|
||||
// 'select-background',
|
||||
// 'settings',
|
||||
// 'shareaudio',
|
||||
// 'sharedvideo',
|
||||
// 'shortcuts',
|
||||
// 'stats',
|
||||
// 'tileview',
|
||||
// 'toggle-camera',
|
||||
// 'videoquality',
|
||||
// '__end'
|
||||
// ],
|
||||
|
||||
// Stats
|
||||
@@ -603,6 +633,9 @@ var config = {
|
||||
// conference (if set to true, these sounds will not be played).
|
||||
// disableJoinLeaveSounds: false,
|
||||
|
||||
// Disables the sounds that play when a chat message is received.
|
||||
// disableIncomingMessageSound: false,
|
||||
|
||||
// Information for the chrome extension banner
|
||||
// chromeExtensionBanner: {
|
||||
// // The chrome extension to be installed address
|
||||
@@ -732,6 +765,9 @@ var config = {
|
||||
// Hides the conference subject
|
||||
// hideConferenceSubject: true,
|
||||
|
||||
// Hides the recording label
|
||||
// hideRecordingLabel: false,
|
||||
|
||||
// Hides the conference timer.
|
||||
// hideConferenceTimer: true,
|
||||
|
||||
|
||||
@@ -17,8 +17,7 @@ import {
|
||||
JitsiConnectionErrors,
|
||||
JitsiConnectionEvents
|
||||
} from './react/features/base/lib-jitsi-meet';
|
||||
import { isVpaasMeeting } from './react/features/billing-counter/functions';
|
||||
import { getJaasJWT } from './react/features/jaas/functions';
|
||||
import { isVpaasMeeting, getJaasJWT } from './react/features/jaas/functions';
|
||||
import { setPrejoinDisplayNameRequired } from './react/features/prejoin/actions';
|
||||
const logger = Logger.getLogger(__filename);
|
||||
|
||||
@@ -89,8 +88,9 @@ export async function connect(id, password, roomName) {
|
||||
const connectionConfig = Object.assign({}, config);
|
||||
const state = APP.store.getState();
|
||||
let { jwt } = state['features/base/jwt'];
|
||||
const { iAmRecorder, iAmSipGateway } = state['features/base/config'];
|
||||
|
||||
if (!jwt && isVpaasMeeting(state)) {
|
||||
if (!iAmRecorder && !iAmSipGateway && !jwt && isVpaasMeeting(state)) {
|
||||
jwt = await getJaasJWT(state);
|
||||
APP.store.dispatch(setJWT(jwt));
|
||||
}
|
||||
|
||||
@@ -100,12 +100,18 @@
|
||||
}
|
||||
|
||||
.audio-preview > div:nth-child(2),
|
||||
.video-preview > div:nth-child(2) {
|
||||
.video-preview > div:nth-child(2),
|
||||
.reactions-menu-popup > div:nth-child(2) {
|
||||
margin-bottom: 4px;
|
||||
outline: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.reactions-menu-popup > div:nth-child(2) {
|
||||
margin-bottom: 6px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
/**
|
||||
* The following selectors keep the chat modal full-size anywhere between 100px
|
||||
* and 580px for desktop or 680px for mobile.
|
||||
|
||||
@@ -4,17 +4,28 @@
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: $drawerZ;
|
||||
border-radius: 16px 16px 0 0;
|
||||
}
|
||||
|
||||
.drawer-portal::after {
|
||||
content: '';
|
||||
background-color: $participantsPaneBgColor;
|
||||
margin-bottom: env(safe-area-inset-bottom, 0);
|
||||
}
|
||||
|
||||
.drawer-menu-container {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.drawer-menu {
|
||||
max-height: 50vh;
|
||||
max-height: calc(80vh - 64px);
|
||||
background: #242528;
|
||||
border-radius: 16px 16px 0 0;
|
||||
overflow-y: auto;
|
||||
|
||||
&.expanded {
|
||||
max-height: 80vh;
|
||||
}
|
||||
overflow-y: hidden;
|
||||
margin-bottom: env(safe-area-inset-bottom, 0);
|
||||
width: 100%;
|
||||
|
||||
.drawer-toggle {
|
||||
display: flex;
|
||||
@@ -42,6 +53,8 @@
|
||||
font-size: 1.2em;
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
height: calc(80vh - 144px - 64px);
|
||||
overflow-y: auto;
|
||||
|
||||
.overflow-menu-item {
|
||||
box-sizing: border-box;
|
||||
|
||||
189
css/_reactions-menu.scss
Normal file
189
css/_reactions-menu.scss
Normal file
@@ -0,0 +1,189 @@
|
||||
@use 'sass:math';
|
||||
|
||||
.reactions-menu {
|
||||
width: 280px;
|
||||
background: #292929;
|
||||
box-shadow: 0px 3px 16px rgba(0, 0, 0, 0.6), 0px 0px 4px 1px rgba(0, 0, 0, 0.25);
|
||||
border-radius: 3px;
|
||||
padding: 16px;
|
||||
|
||||
&.overflow {
|
||||
width: auto;
|
||||
padding-bottom: max(env(safe-area-inset-bottom, 0), 16px);
|
||||
background-color: #141414;
|
||||
box-shadow: none;
|
||||
border-radius: 0;
|
||||
position: relative;
|
||||
|
||||
.toolbox-icon {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
|
||||
span.emoji {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
.reactions-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
|
||||
.toolbox-button {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.toolbox-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 6px;
|
||||
|
||||
span.emoji {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 22px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
.reactions-row {
|
||||
.toolbox-button {
|
||||
margin-right: 8px;
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
.toolbox-button:last-of-type {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.raise-hand-row {
|
||||
margin-top: 16px;
|
||||
|
||||
.toolbox-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.toolbox-icon {
|
||||
width: 100%;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
|
||||
span.text {
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
line-height: 24px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reactions-animations-container {
|
||||
position: absolute;
|
||||
width: 20%;
|
||||
bottom: 0;
|
||||
left: 40%;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.reactions-menu-popup-container,
|
||||
.reactions-menu-popup {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
$reactionCount: 20;
|
||||
|
||||
@function random($min, $max) {
|
||||
@return math.random() * ($max - $min) + $min;
|
||||
}
|
||||
|
||||
.reaction-emoji {
|
||||
position: absolute;
|
||||
font-size: 24px;
|
||||
line-height: 32px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
top: 0;
|
||||
left: 20px;
|
||||
opacity: 0;
|
||||
z-index: 1;
|
||||
|
||||
&.reaction-0 {
|
||||
animation: flowToRight 5s forwards ease-in-out;
|
||||
}
|
||||
|
||||
@for $i from 1 through $reactionCount {
|
||||
&.reaction-#{$i} {
|
||||
animation: animation-#{$i} 5s forwards ease-in-out;
|
||||
top: #{random(-40, 10)}px;
|
||||
left: #{random(0, 30)}px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes flowToRight {
|
||||
0% {
|
||||
transform: translate(0px, 0px) scale(0.6);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: translate(40px, -70vh) scale(1.5);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: translate(40px, -70vh) scale(1.5);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(140px, -50vh) scale(1);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin animation-list {
|
||||
@for $i from 1 through $reactionCount {
|
||||
$topX: random(-100, 100);
|
||||
$topY: random(65, 75);
|
||||
$bottomX: random(150, 200);
|
||||
$bottomY: random(40, 50);
|
||||
|
||||
@if $topX < 0 {
|
||||
$bottomX: -$bottomX;
|
||||
}
|
||||
|
||||
@keyframes animation-#{$i} {
|
||||
0% {
|
||||
transform: translate(0, 0) scale(0.6);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: translate(#{$topX}px, -#{$topY}vh) scale(1.5);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
75% {
|
||||
transform: translate(#{$topX}px, -#{$topY}vh) scale(1.5);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: translate(#{$bottomX}px, -#{$bottomY}vh) scale(1);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include animation-list;
|
||||
@@ -9,8 +9,31 @@
|
||||
z-index: $zindex3;
|
||||
|
||||
&.visible {
|
||||
top: 0px;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
&.recording {
|
||||
top: 0;
|
||||
|
||||
.subject-details-container {
|
||||
opacity: 0;
|
||||
transition: opacity .3s ease-in;
|
||||
}
|
||||
|
||||
.subject-info-container .show-always {
|
||||
transition: margin-left .3s ease-in;
|
||||
}
|
||||
|
||||
&.visible {
|
||||
.subject-details-container {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.subject-details-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.subject-info-container {
|
||||
|
||||
@@ -105,16 +105,23 @@
|
||||
margin: 0 auto;
|
||||
max-width: 100%;
|
||||
pointer-events: all;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.toolbox-content-wrapper::after {
|
||||
content: '';
|
||||
background: $newToolbarBackgroundColor;
|
||||
padding-bottom: env(safe-area-inset-bottom, 0);
|
||||
}
|
||||
|
||||
.toolbox-content-items {
|
||||
background: $newToolbarBackgroundColor;
|
||||
box-shadow: 0px 2px 8px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.15);
|
||||
border-radius: 6px;
|
||||
margin: 0 auto;
|
||||
padding: 6px;
|
||||
text-align: center;
|
||||
pointer-events: all;
|
||||
box-shadow: 0px 2px 8px 4px rgba(0, 0, 0, 0.25), 0px 0px 0px 1px rgba(0, 0, 0, 0.15);
|
||||
|
||||
>div {
|
||||
margin-left: 8px;
|
||||
|
||||
@@ -16,8 +16,8 @@
|
||||
z-index: $subtitlesZ;
|
||||
|
||||
&.lifted {
|
||||
// Lift subtitle above toolbar+invite box.
|
||||
bottom: $newToolbarSize + 112px + 40px;
|
||||
// Lift subtitle above toolbar+dominant speaker box.
|
||||
bottom: $newToolbarSize + 36px + 40px;
|
||||
}
|
||||
|
||||
span {
|
||||
|
||||
@@ -46,6 +46,7 @@ $menuBG:#242528;
|
||||
$newToolbarFontSize: 24px;
|
||||
$newToolbarHangupFontSize: 32px;
|
||||
$newToolbarSize: 48px;
|
||||
$newToolbarSizeMobile: 60px;
|
||||
$newToolbarSizeWithPadding: calc(#{$newToolbarSize} + 24px);
|
||||
$toolbarTitleFontSize: 19px;
|
||||
$overflowMenuItemColor: #fff;
|
||||
|
||||
@@ -122,11 +122,11 @@ body.welcome-page {
|
||||
#moderated-meetings {
|
||||
max-width: calc(100% - 40px);
|
||||
padding: 16px 0 39px 0;
|
||||
margin: $welcomePageEnterRoomMargin;
|
||||
width: $welcomePageEnterRoomWidth;
|
||||
|
||||
p {
|
||||
color: $welcomePageDescriptionColor;
|
||||
float: left;
|
||||
text-align: $welcomePageHeaderTextAlign;
|
||||
|
||||
a {
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transition: margin-bottom .3s ease-in;
|
||||
}
|
||||
|
||||
.filmstrip {
|
||||
@@ -52,11 +53,23 @@
|
||||
margin-left: $sidebarWidth;
|
||||
width: calc(100% - #{$sidebarWidth});
|
||||
|
||||
.remote-videos{
|
||||
.remote-videos {
|
||||
width: calc(100vw - #{$sidebarWidth});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.collapse {
|
||||
#remoteVideos {
|
||||
height: calc(100% - #{$newToolbarSizeMobile}) !important;
|
||||
margin-bottom: $newToolbarSizeMobile;
|
||||
}
|
||||
|
||||
.remote-videos {
|
||||
// !important is needed here as overflow is set via element.style in a FixedSizeGrid.
|
||||
overflow: hidden auto !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
flex-direction: column-reverse;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: ($desktopAppDragBarHeight - 5px) 5px 10px;
|
||||
padding: ($desktopAppDragBarHeight - 5px) 5px calc(env(safe-area-inset-bottom, 0) + 10px);
|
||||
/**
|
||||
* fixed positioning is necessary for remote menus and tooltips to pop
|
||||
* out of the scrolling filmstrip. AtlasKit dialogs and tooltips use
|
||||
|
||||
@@ -106,6 +106,7 @@ $flagsImagePath: "../images/";
|
||||
@import 'connection-status';
|
||||
@import 'drawer';
|
||||
@import 'participants-pane';
|
||||
@import 'reactions-menu';
|
||||
@import 'plan-limit';
|
||||
|
||||
/* Modules END */
|
||||
|
||||
@@ -14,11 +14,7 @@
|
||||
.virtual-background-none:hover {
|
||||
opacity: 0.5;
|
||||
border: 2px solid #99bbf3;
|
||||
@media (min-width: 432px) and (min-width: 432px) and (max-width: 632px) {
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
@media (max-width: 432px) {
|
||||
@media (max-width: 632px) {
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
@@ -87,7 +83,7 @@
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
@media (min-width: 432px) and (max-width: 632px) {
|
||||
@media (max-width: 632px) {
|
||||
font-size: 1.5vw;
|
||||
.desktop-share,
|
||||
.virtual-background-none,
|
||||
@@ -106,29 +102,8 @@
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 432px) {
|
||||
@media (max-width: 360px) {
|
||||
grid-template-columns: auto auto auto;
|
||||
font-size: 1.5vw;
|
||||
.desktop-share,
|
||||
.virtual-background-none,
|
||||
.thumbnail,
|
||||
.blur,
|
||||
.slight-blur {
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
.desktop-share-selected,
|
||||
.thumbnail-selected,
|
||||
.none-selected,
|
||||
.blur-selected,
|
||||
.slight-blur-selected {
|
||||
height: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
}
|
||||
@media (max-width: 320px) {
|
||||
grid-template-columns: auto auto auto;
|
||||
font-size: 1.5vw;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -163,7 +138,7 @@
|
||||
display: none;
|
||||
left: 96;
|
||||
bottom: 51;
|
||||
@media (min-width: 432px) and (max-width: 632px) {
|
||||
@media (max-width: 632px) {
|
||||
left: 51px;
|
||||
}
|
||||
}
|
||||
@@ -196,10 +171,7 @@
|
||||
width: 570px;
|
||||
margin-bottom: 8px;
|
||||
z-index: 2;
|
||||
@media (min-width: 432px) and (max-width: 632px) {
|
||||
max-width: 336;
|
||||
}
|
||||
@media (max-width: 432px) {
|
||||
@media (max-width: 632px) {
|
||||
max-width: 336;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,7 +174,7 @@ var interfaceConfig = {
|
||||
RECENT_LIST_ENABLED: true,
|
||||
REMOTE_THUMBNAIL_RATIO: 1, // 1:1
|
||||
|
||||
SETTINGS_SECTIONS: [ 'devices', 'language', 'moderator', 'profile', 'calendar' ],
|
||||
SETTINGS_SECTIONS: [ 'devices', 'language', 'moderator', 'profile', 'calendar', 'sounds' ],
|
||||
|
||||
/**
|
||||
* Specify which sharing features should be displayed. If the value is not set
|
||||
@@ -208,13 +208,7 @@ var interfaceConfig = {
|
||||
* DEPRECATED!
|
||||
* This config was moved to config.js as `toolbarButtons`.
|
||||
*/
|
||||
// TOOLBAR_BUTTONS: [
|
||||
// 'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',
|
||||
// 'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
|
||||
// 'livestreaming', 'etherpad', 'sharedvideo', 'settings', 'raisehand',
|
||||
// 'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
|
||||
// 'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone', 'security'
|
||||
// ],
|
||||
// TOOLBAR_BUTTONS: [],
|
||||
|
||||
TOOLBAR_TIMEOUT: 4000,
|
||||
|
||||
|
||||
@@ -58,7 +58,9 @@ target 'JitsiMeetSDK' do
|
||||
pod 'react-native-calendar-events', :path => '../node_modules/react-native-calendar-events'
|
||||
pod 'react-native-keep-awake', :path => '../node_modules/react-native-keep-awake'
|
||||
pod 'react-native-netinfo', :path => '../node_modules/@react-native-community/netinfo'
|
||||
pod 'react-native-slider', :path => '../node_modules/@react-native-community/slider'
|
||||
pod 'react-native-splash-screen', :path => '../node_modules/react-native-splash-screen'
|
||||
pod 'react-native-video', :path => '../node_modules/react-native-video/react-native-video.podspec'
|
||||
pod 'react-native-webview', :path => '../node_modules/react-native-webview'
|
||||
pod 'react-native-webrtc', :path => '../node_modules/react-native-webrtc'
|
||||
pod 'RNCAsyncStorage', :path => '../node_modules/@react-native-async-storage/async-storage'
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
PODS:
|
||||
- AppAuth (1.2.0):
|
||||
- AppAuth/Core (= 1.2.0)
|
||||
- AppAuth/ExternalUserAgent (= 1.2.0)
|
||||
- AppAuth/Core (1.2.0)
|
||||
- AppAuth/ExternalUserAgent (1.2.0)
|
||||
- AppAuth (1.4.0):
|
||||
- AppAuth/Core (= 1.4.0)
|
||||
- AppAuth/ExternalUserAgent (= 1.4.0)
|
||||
- AppAuth/Core (1.4.0)
|
||||
- AppAuth/ExternalUserAgent (1.4.0)
|
||||
- boost-for-react-native (1.63.0)
|
||||
- CocoaLumberjack (3.5.3):
|
||||
- CocoaLumberjack/Core (= 3.5.3)
|
||||
@@ -48,7 +48,7 @@ PODS:
|
||||
- GoogleUtilities/Environment (~> 6.7)
|
||||
- GoogleUtilities/Logger (~> 6.7)
|
||||
- nanopb (~> 1.30906.0)
|
||||
- FirebaseCrashlytics (4.6.1):
|
||||
- FirebaseCrashlytics (4.6.2):
|
||||
- FirebaseCore (~> 6.10)
|
||||
- FirebaseInstallations (~> 1.6)
|
||||
- GoogleDataTransport (~> 7.2)
|
||||
@@ -77,9 +77,9 @@ PODS:
|
||||
- GoogleUtilities/Network (~> 6.7)
|
||||
- "GoogleUtilities/NSData+zlib (~> 6.7)"
|
||||
- nanopb (~> 1.30906.0)
|
||||
- GoogleDataTransport (7.4.0):
|
||||
- GoogleDataTransport (7.5.1):
|
||||
- nanopb (~> 1.30906.0)
|
||||
- GoogleSignIn (5.0.1):
|
||||
- GoogleSignIn (5.0.2):
|
||||
- AppAuth (~> 1.2)
|
||||
- GTMAppAuth (~> 1.0)
|
||||
- GTMSessionFetcher/Core (~> 1.1)
|
||||
@@ -102,21 +102,17 @@ PODS:
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/UserDefaults (6.7.2):
|
||||
- GoogleUtilities/Logger
|
||||
- GTMAppAuth (1.0.0):
|
||||
- AppAuth/Core (~> 1.0)
|
||||
- GTMSessionFetcher (~> 1.1)
|
||||
- GTMSessionFetcher (1.2.2):
|
||||
- GTMSessionFetcher/Full (= 1.2.2)
|
||||
- GTMSessionFetcher/Core (1.2.2)
|
||||
- GTMSessionFetcher/Full (1.2.2):
|
||||
- GTMSessionFetcher/Core (= 1.2.2)
|
||||
- GTMAppAuth (1.2.2):
|
||||
- AppAuth/Core (~> 1.4)
|
||||
- GTMSessionFetcher/Core (~> 1.5)
|
||||
- GTMSessionFetcher/Core (1.6.1)
|
||||
- nanopb (1.30906.0):
|
||||
- nanopb/decode (= 1.30906.0)
|
||||
- nanopb/encode (= 1.30906.0)
|
||||
- nanopb/decode (1.30906.0)
|
||||
- nanopb/encode (1.30906.0)
|
||||
- ObjectiveDropboxOfficial (3.9.4)
|
||||
- PromisesObjC (1.2.10)
|
||||
- PromisesObjC (1.2.12)
|
||||
- RCTRequired (0.61.5-jitsi.2)
|
||||
- RCTTypeSafety (0.61.5-jitsi.2):
|
||||
- FBLazyVector (= 0.61.5-jitsi.2)
|
||||
@@ -288,9 +284,16 @@ PODS:
|
||||
- React
|
||||
- react-native-netinfo (4.1.5):
|
||||
- React
|
||||
- react-native-slider (3.0.3):
|
||||
- React
|
||||
- react-native-splash-screen (3.2.0):
|
||||
- React
|
||||
- react-native-webrtc (1.89.1):
|
||||
- react-native-video (5.1.1):
|
||||
- React-Core
|
||||
- react-native-video/Video (= 5.1.1)
|
||||
- react-native-video/Video (5.1.1):
|
||||
- React-Core
|
||||
- react-native-webrtc (1.92.0):
|
||||
- React-Core
|
||||
- react-native-webview (11.0.2):
|
||||
- React-Core
|
||||
@@ -348,8 +351,8 @@ PODS:
|
||||
- React-jsi (= 0.61.5-jitsi.2)
|
||||
- ReactCommon/jscallinvoker (= 0.61.5-jitsi.2)
|
||||
- ReactCommon/turbomodule/core (= 0.61.5-jitsi.2)
|
||||
- RNCAsyncStorage (1.13.2):
|
||||
- React
|
||||
- RNCAsyncStorage (1.15.5):
|
||||
- React-Core
|
||||
- RNDefaultPreference (1.4.2):
|
||||
- React
|
||||
- RNDeviceInfo (8.0.0):
|
||||
@@ -394,7 +397,9 @@ DEPENDENCIES:
|
||||
- react-native-calendar-events (from `../node_modules/react-native-calendar-events`)
|
||||
- react-native-keep-awake (from `../node_modules/react-native-keep-awake`)
|
||||
- "react-native-netinfo (from `../node_modules/@react-native-community/netinfo`)"
|
||||
- "react-native-slider (from `../node_modules/@react-native-community/slider`)"
|
||||
- react-native-splash-screen (from `../node_modules/react-native-splash-screen`)
|
||||
- react-native-video (from `../node_modules/react-native-video/react-native-video.podspec`)
|
||||
- react-native-webrtc (from `../node_modules/react-native-webrtc`)
|
||||
- react-native-webview (from `../node_modules/react-native-webview`)
|
||||
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
|
||||
@@ -475,8 +480,12 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-keep-awake"
|
||||
react-native-netinfo:
|
||||
:path: "../node_modules/@react-native-community/netinfo"
|
||||
react-native-slider:
|
||||
:path: "../node_modules/@react-native-community/slider"
|
||||
react-native-splash-screen:
|
||||
:path: "../node_modules/react-native-splash-screen"
|
||||
react-native-video:
|
||||
:path: "../node_modules/react-native-video/react-native-video.podspec"
|
||||
react-native-webrtc:
|
||||
:path: "../node_modules/react-native-webrtc"
|
||||
react-native-webview:
|
||||
@@ -519,7 +528,7 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native/ReactCommon/yoga"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
AppAuth: bce82c76043657c99d91e7882e8a9e1a93650cd4
|
||||
AppAuth: 31bcec809a638d7bd2f86ea8a52bd45f6e81e7c7
|
||||
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
|
||||
CocoaLumberjack: 2f44e60eb91c176d471fdba43b9e3eae6a721947
|
||||
DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
|
||||
@@ -529,20 +538,20 @@ SPEC CHECKSUMS:
|
||||
FirebaseAnalytics: 5dd088bd2e67bb9d13dbf792d1164ceaf3052193
|
||||
FirebaseCore: d889d9e12535b7f36ac8bfbf1713a0836a3012cd
|
||||
FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1
|
||||
FirebaseCrashlytics: 5777d3462fb8c3ab9e80a2473bd7d667a2e8411c
|
||||
FirebaseCrashlytics: 1a747c9cc084a24dc6d9511c991db1cd078154eb
|
||||
FirebaseDynamicLinks: 6eac37d86910382eafb6315d952cc44c9e176094
|
||||
FirebaseInstallations: 466c7b4d1f58fe16707693091da253726a731ed2
|
||||
Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
|
||||
glog: 1f3da668190260b06b429bb211bfbee5cd790c28
|
||||
GoogleAppMeasurement: 966e88df9d19c15715137bb2ddaf52373f111436
|
||||
GoogleDataTransport: b7f406340a291370045a270c599e53c6fa6ec20f
|
||||
GoogleSignIn: 3a51b9bb8e48b635fd7f4272cee06ca260345b86
|
||||
GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833
|
||||
GoogleSignIn: 7137d297ddc022a7e0aa4619c86d72c909fa7213
|
||||
GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3
|
||||
GTMAppAuth: 4deac854479704f348309e7b66189e604cf5e01e
|
||||
GTMSessionFetcher: 61bb0f61a4cb560030f1222021178008a5727a23
|
||||
GTMAppAuth: ad5c2b70b9a8689e1a04033c9369c4915bfcbe89
|
||||
GTMSessionFetcher: 36689134877faeb055b27dfa4ccc9ceaa42e029e
|
||||
nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
|
||||
ObjectiveDropboxOfficial: a5afefc83f6467c42c45f2253f583f2ad1ffc701
|
||||
PromisesObjC: b14b1c6b68e306650688599de8a45e49fae81151
|
||||
PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97
|
||||
RCTRequired: a686731276578c125dff205f08b6ec9cee6ede32
|
||||
RCTTypeSafety: 88e5500e801c00d16a3d1895e3470d13beed6584
|
||||
React: 8b2bcf6a93846e47a7a365a54ec6edeb78b37701
|
||||
@@ -556,8 +565,10 @@ SPEC CHECKSUMS:
|
||||
react-native-calendar-events: 1442fad71a00388f933cfa25512588fec300fcf8
|
||||
react-native-keep-awake: eba3137546b10003361b37c761f6c429b59814ae
|
||||
react-native-netinfo: 8d8db463bcc5db66a8ac5c48a7d86beb3b92f61a
|
||||
react-native-slider: b733e17fdd31186707146debf1f04b5d94aa1a93
|
||||
react-native-splash-screen: 200d11d188e2e78cea3ad319964f6142b6384865
|
||||
react-native-webrtc: ccb0c21eb4fb04326648fbdb4a5d49977e2cf274
|
||||
react-native-video: 1574074179ecaf6a9dd067116c8f31bf9fec15c8
|
||||
react-native-webrtc: bbb644859dcc37ccb7edaec860ca62ed47bf996c
|
||||
react-native-webview: b2542d6fd424bcc3e3b2ec5f854f0abb4ec86c87
|
||||
React-RCTActionSheet: bcbc311dc3b47bc8efb2737ff0940239a45789a9
|
||||
React-RCTAnimation: 65f61080ce632f6dea23d52e354ffac9948396c6
|
||||
@@ -569,7 +580,7 @@ SPEC CHECKSUMS:
|
||||
React-RCTText: 4f1b99f228278d2a5e9008eced8dc9c974c4a270
|
||||
React-RCTVibration: c1041024893fdfdb8371e7c720c437751b711676
|
||||
ReactCommon: 18014e1d98dbeb9141e935cfe35fc93bd511ffb6
|
||||
RNCAsyncStorage: bc2f81cc1df90c267ce9ed30bb2dbc93b945a8ee
|
||||
RNCAsyncStorage: 8324611026e8dc3706f829953aa6e3899f581589
|
||||
RNDefaultPreference: 56a405ce61033ac77b95004dccd7ac54c2eb50d1
|
||||
RNDeviceInfo: 72ded653ce636b3f03571e90bed99309a714944e
|
||||
RNGoogleSignin: 39336070b35fc4cea6a98cf111e00480317be0ae
|
||||
@@ -578,6 +589,6 @@ SPEC CHECKSUMS:
|
||||
RNWatch: a5320c959c75e72845c07985f3e935e58998f1d3
|
||||
Yoga: 96b469c5e81ff51b917b92e8c3390642d4ded30c
|
||||
|
||||
PODFILE CHECKSUM: d059cebf82da14a53940a16c24c3330752d4b0c8
|
||||
PODFILE CHECKSUM: f4db44d934caeae7212dbaa33abe62ed164363e8
|
||||
|
||||
COCOAPODS: 1.10.1
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>21.2.0</string>
|
||||
<string>21.3.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSExtension</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>21.2.0</string>
|
||||
<string>21.3.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.2.0</string>
|
||||
<string>21.3.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.2.0</string>
|
||||
<string>21.3.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>CLKComplicationPrincipalClass</key>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>3.6.0</string>
|
||||
<string>3.8.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -59,6 +59,16 @@
|
||||
|
||||
#pragma mark - Utility methods
|
||||
|
||||
/**
|
||||
* Once the react native bridge is destroyed you are responsible for reinstantiating it back. Use this method to do so.
|
||||
*/
|
||||
- (void)instantiateReactNativeBridge;
|
||||
|
||||
/**
|
||||
* Helper method to destroy the react native bridge, cleaning up resources in the process. Once the react native bridge is destroyed you are responsible for reinstantiating it back using `instantiateReactNativeBridge` method.
|
||||
*/
|
||||
- (void)destroyReactNativeBridge;
|
||||
|
||||
- (JitsiMeetConferenceOptions *_Nonnull)getInitialConferenceOptions;
|
||||
|
||||
- (BOOL)isCrashReportingDisabled;
|
||||
|
||||
@@ -28,7 +28,6 @@
|
||||
#import <RNGoogleSignin/RNGoogleSignin.h>
|
||||
#import <WebRTC/RTCLogging.h>
|
||||
|
||||
|
||||
@implementation JitsiMeet {
|
||||
RCTBridgeWrapper *_bridgeWrapper;
|
||||
NSDictionary *_launchOptions;
|
||||
@@ -50,7 +49,7 @@
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
// Initialize the on and only bridge for interfacing with React Native.
|
||||
// Initialize the one and only bridge for interfacing with React Native.
|
||||
_bridgeWrapper = [[RCTBridgeWrapper alloc] init];
|
||||
|
||||
// Initialize the listener for handling start/stop screensharing notifications.
|
||||
@@ -119,6 +118,18 @@
|
||||
|
||||
#pragma mark - Utility methods
|
||||
|
||||
- (void)instantiateReactNativeBridge {
|
||||
if (_bridgeWrapper != nil) {
|
||||
return;
|
||||
};
|
||||
|
||||
_bridgeWrapper = [[RCTBridgeWrapper alloc] init];
|
||||
}
|
||||
|
||||
- (void)destroyReactNativeBridge {
|
||||
_bridgeWrapper = nil;
|
||||
}
|
||||
|
||||
- (JitsiMeetConferenceOptions *)getInitialConferenceOptions {
|
||||
if (_launchOptions[UIApplicationLaunchOptionsURLKey]) {
|
||||
NSURL *url = _launchOptions[UIApplicationLaunchOptionsURLKey];
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
"shareInvite": "Einladung zur Versammlung teilen",
|
||||
"shareLink": "Teilen Sie den Konferenzlink, um andere einzuladen",
|
||||
"shareStream": "Den Livestreaminglink freigeben",
|
||||
"sip": "SIP: {{address}}",
|
||||
"sipAddresses": "SIP-Adressen",
|
||||
"telephone": "Telefon: {{number}}",
|
||||
"title": "Personen zu dieser Konferenz einladen",
|
||||
"yahooEmail": "Yahoo-E-Mail"
|
||||
@@ -215,6 +215,7 @@
|
||||
"gracefulShutdown": "Der Dienst steht momentan wegen Wartungsarbeiten nicht zur Verfügung. Bitte versuchen Sie es später noch einmal.",
|
||||
"grantModeratorDialog": "Möchten Sie wirklich Moderationsrechte an diese Person vergeben?",
|
||||
"grantModeratorTitle": "Moderationsrechte vergeben",
|
||||
"hideShareAudioHelper": "Diese Meldung nicht mehr anzeigen",
|
||||
"IamHost": "Ich leite das Meeting",
|
||||
"incorrectRoomLockPassword": "Falsches Passwort",
|
||||
"incorrectPassword": "Name oder Passwort ungültig",
|
||||
@@ -257,16 +258,21 @@
|
||||
"muteParticipantBody": "Sie können die Stummschaltung anderer Personen nicht aufheben, aber eine Person kann ihre eigene Stummschaltung jederzeit beenden.",
|
||||
"muteParticipantButton": "Stummschalten",
|
||||
"muteParticipantDialog": "Wollen Sie diese Person wirklich stummschalten? Sie können die Stummschaltung nicht wieder aufheben, die Person kann dies aber jederzeit selbst tun.",
|
||||
"muteParticipantsVideoDialog": "Wollen Sie die Kamera dieser Person wirklich deaktivieren? Sie können die Kamera nicht wieder aktivieren, die Person kann dies aber jederzeit selbst tun.",
|
||||
"muteParticipantTitle": "Person stummschalten?",
|
||||
"muteParticipantsVideoButton": "Kamera ausschalten",
|
||||
"muteParticipantsVideoTitle": "Die Kamera von dieser Person ausschalten?",
|
||||
"muteParticipantsVideoBody": "Sie können die Kamera nicht wieder aktivieren, die Teilnehmer können dies aber jederzeit wieder ändern.",
|
||||
"noDropboxToken": "Kein gültiges Dropbox-Token",
|
||||
"Ok": "OK",
|
||||
"password": "Passwort",
|
||||
"passwordLabel": "Dieses Meeting wurde gesichert. Bitte geben Sie das $t(lockRoomPasswordUppercase) ein, um dem Meeting beizutreten.",
|
||||
"passwordNotSupported": "Das Festlegen eines Konferenzpassworts wird nicht unterstützt.",
|
||||
"passwordNotSupportedTitle": "$t(lockRoomPasswordUppercase) nicht unterstützt",
|
||||
"passwordRequired": "$t(lockRoomPasswordUppercase) erforderlich",
|
||||
"permissionErrorTitle": "Berechtigung benötigt",
|
||||
"permissionCameraRequiredError": "Der Zugriff auf die Kamera wird benötigt, um in Videokonferenzen teilzunehmen. Bitte in den Einstellungen zulassen",
|
||||
"permissionMicRequiredError": "Der Zugriff auf das Mikrofon wird benötigt, um an Konferenzen mit Ton teilzunehmen. Bitte in den Einstellungen zulassen",
|
||||
"popupError": "Ihr Browser blockiert Pop-ups von dieser Website. Bitte aktivieren Sie Pop-ups in den Sicherheitseinstellungen des Browsers und versuchen Sie es erneut.",
|
||||
"popupErrorTitle": "Pop-up blockiert",
|
||||
"readMore": "mehr",
|
||||
@@ -300,6 +306,13 @@
|
||||
"sessTerminated": "Konferenz beendet",
|
||||
"sessionRestarted": "Konferenz neugestartet",
|
||||
"Share": "Teilen",
|
||||
"shareAudio": "Fortfahren",
|
||||
"shareAudioTitle" : "Wie kann Audio geteilt werden",
|
||||
"shareAudioWarningTitle": "Sie müssen die Bildschirmfreigabe ausschalten, bevor Sie Audio teilen können",
|
||||
"shareAudioWarningH1": "Wenn Sie Ihr Audio teilen wollen:",
|
||||
"shareAudioWarningD1": "müssen Sie Ihre Bildschirmfreigabe stoppen, bevor Sie Audio teilen können.",
|
||||
"shareAudioWarningD2": "müssen Sie Ihre Bildschirmfreigabe neustarten und die Option \"Audio freigeben\" auswählen.",
|
||||
"shareMediaWarningGenericH2": "Wenn Sie Ihren Bildschirm und Audio teilen wollen",
|
||||
"shareVideoLinkError": "Bitte einen gültigen YouTube-Link angeben.",
|
||||
"shareVideoTitle": "Video teilen",
|
||||
"shareYourScreen": "Bildschirmfreigabe ein-/ausschalten",
|
||||
@@ -307,6 +320,10 @@
|
||||
"startLiveStreaming": "Livestream starten",
|
||||
"startRecording": "Aufnahme starten",
|
||||
"startRemoteControlErrorMessage": "Beim Versuch, die Fernsteuerung zu starten, ist ein Fehler aufgetreten!",
|
||||
"shareScreenWarningTitle": "Sie müssen die Audiofreigabe beenden, bevor Sie den Bildschirm freigeben können",
|
||||
"shareScreenWarningH1": "Wenn Sie Ihren Bildschirm freigeben wollen:",
|
||||
"shareScreenWarningD1": "müssen Sie Ihre Audiofreigabe stoppen, bevor Sie ihren Bildschirm freigeben.",
|
||||
"shareScreenWarningD2": "müssen Sie Ihre Audiofreigabe stoppen und dann die Bildschirmfreigabe mit der Option \"Audio freigeben\" starten.",
|
||||
"stopLiveStreaming": "Livestream stoppen",
|
||||
"stopRecording": "Aufnahme stoppen",
|
||||
"stopRecordingWarning": "Sind Sie sicher, dass Sie die Aufnahme stoppen möchten?",
|
||||
@@ -323,6 +340,9 @@
|
||||
"userIdentifier": "Benutzername",
|
||||
"userPassword": "Passwort",
|
||||
"videoLink": "Video-Link",
|
||||
"viewUpgradeOptions": "Upgradeoptionen anzeigen",
|
||||
"viewUpgradeOptionsContent": "Sie müssen Ihren Tarif erweitern, um Premium-Features wie Aufnahme, Transkription, RTMP-Streaming und mehr zu nutzen.",
|
||||
"viewUpgradeOptionsTitle": "Sie haben ein Premium-Feature entdeckt!",
|
||||
"WaitForHostMsg": "Die Konferenz <b>{{room}}</b> wurde noch nicht gestartet. Falls Sie die Konferenz leiten, authentifizieren Sie sich bitte. Warten Sie andernfalls, bis die Konferenz gestartet wird.",
|
||||
"WaitForHostMsgWOk": "Die Konferenz <b>{{room}}</b> wurde noch nicht gestartet. Falls Sie die Konferenz leiten, authentifizieren Sie sich bitte. Warten Sie andernfalls, bis die Konferenz gestartet wird.",
|
||||
"WaitingForHostTitle": "Warten auf den Beginn der Konferenz …",
|
||||
@@ -344,11 +364,11 @@
|
||||
"title": "Diese Konferenz einbetten"
|
||||
},
|
||||
"virtualBackground": {
|
||||
"apply": "Anwenden",
|
||||
"title": "Hintergründe",
|
||||
"blur": "Hintergrund unscharf",
|
||||
"slightBlur": "Hintergrund leicht unscharf",
|
||||
"removeBackground": "Hintergrund entfernen",
|
||||
"uploadImage": "Bild hochladen",
|
||||
"addBackground": "Hintergrund hinzufügen",
|
||||
"pleaseWait": "Bitte warten...",
|
||||
"none": "keiner",
|
||||
@@ -362,7 +382,8 @@
|
||||
"image6" : "Wald",
|
||||
"image7" : "Sonnenaufgang",
|
||||
"desktopShareError": "Desktop konnte nicht freigegeben werden",
|
||||
"desktopShare":"Desktopfreigabe"
|
||||
"desktopShare": "Desktopfreigabe",
|
||||
"webAssemblyWarning": "WebAssembly wird nicht unterstützt"
|
||||
},
|
||||
"feedback": {
|
||||
"average": "Durchschnittlich",
|
||||
@@ -399,6 +420,10 @@
|
||||
"invitePhone": "Wenn Sie stattdessen per Telefon beitreten möchten, wählen sie: {{number}},,{{conferenceID}}#\n",
|
||||
"invitePhoneAlternatives": "Suchen Sie nach einer anderen Einwahlnummer ?\nEinwahlnummern der Konferenz anzeigen: {{url}}\n\n\nWenn Sie sich auch über ein Raumtelefon einwählen, nehmen Sie teil, ohne sich mit dem Ton zu verbinden: {{silentUrl}}",
|
||||
"inviteSipEndpoint": "Um mit SIP teilzunehmen, folgende Adresse nutzen: {{sipUri}}",
|
||||
"inviteTextiOSPersonal": "{{name}} lädt Sie zu einem Meeting ein.",
|
||||
"inviteTextiOSJoinSilent": "Wenn Sie über ein Konferenztelefon teilnehmen, können Sie diesen Link nutzen um ohne Ton an der Konferenz teilzunehmen: {{silentUrl}}.",
|
||||
"inviteTextiOSInviteUrl": "Am Meeting teilnehmen: {{inviteUrl}}.",
|
||||
"inviteTextiOSPhone": "Nutzen Sie folgende Nummer um via Telefon teilzunehmen: {{number}},,{{conferenceID}}#. Wenn Sie nach einer anderen Einwahlnummer suchen, finden Sie die vollständige Liste hier: {{didUrl}}.",
|
||||
"inviteURLFirstPartGeneral": "Sie wurden zur Teilnahme an einem Meeting eingeladen.",
|
||||
"inviteURLFirstPartPersonal": "{{name}} lädt Sie zu einem Meeting ein.\n",
|
||||
"inviteURLSecondPart": "\nAm Meeting teilnehmen:\n{{url}}\n",
|
||||
@@ -409,6 +434,7 @@
|
||||
"noRoom": "Keine Konferenz für die Einwahlinformationen angegeben.",
|
||||
"numbers": "Einwahlnummern",
|
||||
"password": "$t(lockRoomPasswordUppercase):",
|
||||
"sip": "SIP-Adresse",
|
||||
"title": "Teilen",
|
||||
"tooltip": "Freigabe-Link und Einwahlinformationen für dieses Meeting",
|
||||
"label": "Einwahlinformationen"
|
||||
@@ -519,6 +545,7 @@
|
||||
"focus": "Konferenzleitung",
|
||||
"focusFail": "{{component}} ist im Moment nicht verfügbar – wiederholen in {{ms}} Sekunden",
|
||||
"grantedTo": "Moderationsrechte an {{to}} vergeben!",
|
||||
"hostAskedUnmute": "Die Moderation bittet Sie, das Mikrofon zu aktivieren",
|
||||
"invitedOneMember": "{{name}} wurde eingeladen",
|
||||
"invitedThreePlusMembers": "{{name}} und {{count}} andere wurden eingeladen",
|
||||
"invitedTwoMembers": "{{first}} und {{second}} wurden eingeladen",
|
||||
@@ -534,7 +561,7 @@
|
||||
"passwordRemovedRemotely": "$t(lockRoomPasswordUppercase) von einer anderen Person entfernt",
|
||||
"passwordSetRemotely": "$t(lockRoomPasswordUppercase) von einer anderen Person gesetzt",
|
||||
"raisedHand": "{{name}} möchte sprechen.",
|
||||
"screenShareNoAudio": " Share audio box was not checked in the window selection screen.",
|
||||
"screenShareNoAudio": "Die Option \"Audio freigeben\" wurde bei der Auswahl des Fensters nicht ausgewählt.",
|
||||
"screenShareNoAudioTitle": "Share audio was not checked",
|
||||
"somebody": "Jemand",
|
||||
"startSilentTitle": "Sie sind ohne Audioausgabe beigetreten!",
|
||||
@@ -549,28 +576,40 @@
|
||||
"oldElectronClientDescription1": "Sie scheinen eine alte Version des Jitsi-Meet-Clients zu nutzen. Diese hat bekannte Schwachstellen. Bitte aktualisieren Sie auf unsere ",
|
||||
"oldElectronClientDescription2": "aktuelle Version",
|
||||
"oldElectronClientDescription3": "!",
|
||||
"moderationInEffectDescription": "Bitte melden um zu sprechen",
|
||||
"moderationInEffectCSDescription": "Bitte melden um ein Video zu teilen",
|
||||
"moderationInEffectVideoDescription": "Bitte melden um die Kamera zu starten",
|
||||
"moderationInEffectTitle": "Das Mikrofon ist von der Moderation gesperrt",
|
||||
"moderationInEffectCSTitle": "Die Videofreigabe ist von der Moderation gesperrt",
|
||||
"moderationInEffectVideoTitle": "Die Kamera ist von der Moderation gesperrt",
|
||||
"moderationRequestFromModerator": "Die Moderation bittet Sie, das Mikrofon zu aktivieren",
|
||||
"moderationRequestFromParticipant": "möchte sprechen",
|
||||
"moderationStartedTitle": "Moderation gestartet",
|
||||
"moderationStoppedTitle": "Moderation gestoppt",
|
||||
"moderationToggleDescription": "von {{participantDisplayName}}",
|
||||
"raiseHandAction": "Melden",
|
||||
"groupTitle": "Benachrichtigungen"
|
||||
},
|
||||
"participantsPane": {
|
||||
"close": "Schließen",
|
||||
"header": "Anwesende",
|
||||
"headings": {
|
||||
"lobby": "Lobby ({{count}})",
|
||||
"participantsList": "Teilnehmer ({{count}})"
|
||||
},
|
||||
"actions": {
|
||||
"muteAll": "Alle stummschalten",
|
||||
"stopVideo": "Video stoppen"
|
||||
}
|
||||
},
|
||||
"participantsPane": {
|
||||
"headings": {
|
||||
"lobby": "Lobby ({{count}})",
|
||||
"participantsList": "Anwesende ({{count}})"
|
||||
"participantsList": "Anwesende ({{count}})",
|
||||
"waitingLobby": "In der Lobby ({{count}})"
|
||||
},
|
||||
"actions": {
|
||||
"allow": "Anwesenden erlauben:",
|
||||
"blockEveryoneMicCamera": "Kamera und Mikrofon von allen sperren",
|
||||
"invite": "Person einladen",
|
||||
"askUnmute": "Anfragen, Stummschaltung aufzuheben",
|
||||
"mute": "Stummschalten",
|
||||
"muteAll": "Alle stummschalten",
|
||||
"stopVideo": "Kamera ausschalten"
|
||||
"muteEveryoneElse": "Alle anderen stummschalten",
|
||||
"startModeration": "Stummschaltung aufheben oder Kamera aktivieren",
|
||||
"stopEveryonesVideo": "Alle Kameras ausschalten",
|
||||
"stopVideo": "Kamera ausschalten",
|
||||
"unblockEveryoneMicCamera": "Kamera und Mikrofon von allen entsperren"
|
||||
}
|
||||
},
|
||||
"passwordSetRemotely": "von einer anderen Person gesetzt",
|
||||
@@ -625,9 +664,9 @@
|
||||
"linkCopied": "Link in die Zwischenablage kopiert",
|
||||
"lookGood": "Ihr Mikrofon scheint zu funktionieren.",
|
||||
"or": "oder",
|
||||
"keyboardShortcuts" : "Tastaturkurzbefehle aktivieren",
|
||||
"premeeting": "Vorschau",
|
||||
"showScreen": "Konferenzvorschau aktivieren",
|
||||
"keyboardShortcuts" : "Tastaturkurzbefehle aktivieren",
|
||||
"startWithPhone": "Mit Telefonaudio starten",
|
||||
"screenSharingError": "Fehler bei Bildschirmfreigabe:",
|
||||
"videoOnlyError": "Videofehler:",
|
||||
@@ -664,12 +703,15 @@
|
||||
"beta": "BETA",
|
||||
"busy": "Es werden Ressourcen für eine Aufnahme bereitgestellt. Bitte in ein paar Minuten erneut versuchen.",
|
||||
"busyTitle": "Alle Aufnahme-Instanzen sind in Gebrauch",
|
||||
"copyLink": "Link kopieren",
|
||||
"error": "Die Aufzeichnung ist fehlgeschlagen. Bitte versuchen Sie es erneut.",
|
||||
"errorFetchingLink": "Der Link zur Aufzeichnung konnte nicht geladen werden.",
|
||||
"expandedOff": "Aufzeichnung wurde gestoppt",
|
||||
"expandedOn": "Das Meeting wird momentan aufgezeichnet.",
|
||||
"expandedPending": "Aufzeichnung wird gestartet…",
|
||||
"failedToStart": "Die Aufnahme konnte nicht gestartet werden",
|
||||
"fileSharingdescription": "Aufzeichnung mit den Personen der Konferenz teilen",
|
||||
"linkGenerated": "Link zur Aufzeichnung wurde generiert.",
|
||||
"live": "LIVE",
|
||||
"loggedIn": "Als {{userName}} angemeldet",
|
||||
"off": "Aufnahme gestoppt",
|
||||
@@ -684,7 +726,8 @@
|
||||
"signIn": "Anmelden",
|
||||
"signOut": "Abmelden",
|
||||
"unavailable": "Oh! Der {{serviceName}} ist aktuell nicht verfügbar. Wir arbeiten an der Behebung des Problems. Bitte versuchen Sie es später noch einmal.",
|
||||
"unavailableTitle": "Aufnahme nicht verfügbar"
|
||||
"unavailableTitle": "Aufnahme nicht verfügbar",
|
||||
"uploadToCloud": "In die Cloud hochladen"
|
||||
},
|
||||
"sectionList": {
|
||||
"pullToRefresh": "Ziehen, um zu aktualisieren"
|
||||
@@ -703,8 +746,13 @@
|
||||
"signedIn": "Momentan wird auf Kalendertermine von {{email}} zugegriffen. Klicken Sie auf die folgende Schaltfläche „Trennen“, um den Zugriff auf die Kalendertermine zu stoppen.",
|
||||
"title": "Kalender"
|
||||
},
|
||||
"desktopShareFramerate": "Framerate für Bildschirmfreigabe",
|
||||
"desktopShareWarning": "Sie müssen die Bildschirmfreigabe neustarten, damit die Einstellung übernommen wird.",
|
||||
"desktopShareHighFpsWarning": "Eine höhere Framerate könnte sich auf Ihre Datenrate auswirken. Sie müssen die Bildschirmfreigabe neustarten, damit die Einstellung übernommen wird.",
|
||||
"devices": "Geräte",
|
||||
"followMe": "Follow-me für alle Personen",
|
||||
"framesPerSecond": "FPS",
|
||||
"incomingMessage": "Eingehende Nachricht",
|
||||
"language": "Sprache",
|
||||
"loggedIn": "Als {{name}} angemeldet",
|
||||
"microphones": "Mikrofon",
|
||||
@@ -712,12 +760,18 @@
|
||||
"more": "Mehr",
|
||||
"name": "Name",
|
||||
"noDevice": "Kein",
|
||||
"participantJoined": "Neue Person nimmt teil",
|
||||
"participantLeft": "Person verlässt die Konferenz",
|
||||
"playSounds": "Hinweistöne aktiviert",
|
||||
"sameAsSystem": "Wie System ({{label}})",
|
||||
"selectAudioOutput": "Audioausgabe",
|
||||
"selectCamera": "Kamera",
|
||||
"selectMic": "Mikrofon",
|
||||
"sounds": "Hinweistöne",
|
||||
"speakers": "Lautsprecher",
|
||||
"startAudioMuted": "Alle Personen treten stumm geschaltet bei",
|
||||
"startAudioMuted": "Alle Personen treten stummgeschaltet bei",
|
||||
"startVideoMuted": "Alle Personen treten ohne Video bei",
|
||||
"talkWhileMuted": "Wenn bei Stummschaltung gesprochen wird",
|
||||
"title": "Einstellungen"
|
||||
},
|
||||
"settingsView": {
|
||||
@@ -766,12 +820,14 @@
|
||||
"title": "Die Konferenz wurde unterbrochen, weil der Standby-Modus aktiviert wurde."
|
||||
},
|
||||
"toolbar": {
|
||||
"accessibilityLabel": {
|
||||
"accessibilityLabel": {
|
||||
"audioOnly": "„Nur Audio“ ein-/ausschalten",
|
||||
"audioRoute": "Audiogerät auswählen",
|
||||
"boo": "Buhen",
|
||||
"callQuality": "Qualitätseinstellungen",
|
||||
"cc": "Untertitel ein-/ausschalten",
|
||||
"chat": "Chatfenster öffnen / schließen",
|
||||
"clap": "Klatschen",
|
||||
"document": "Geteiltes Dokument schließen",
|
||||
"download": "Unsere Apps herunterladen",
|
||||
"embedMeeting": "Konferenz einbetten",
|
||||
@@ -782,6 +838,8 @@
|
||||
"help": "Hilfe",
|
||||
"invite": "Person einladen",
|
||||
"kick": "Person entfernen",
|
||||
"laugh": "Lachen",
|
||||
"like": "Daumen nach oben",
|
||||
"lobbyButton": "Lobbymodus ein-/ausschalten",
|
||||
"localRecording": "Lokale Aufzeichnungssteuerelemente ein-/ausschalten",
|
||||
"lockRoom": "Konferenzpasswort ein-/ausschalten",
|
||||
@@ -794,10 +852,12 @@
|
||||
"muteEveryonesVideo": "Alle Kameras ausschalten",
|
||||
"muteEveryoneElsesVideo": "Alle anderen Kameras ausschalten",
|
||||
"participants": "Anwesende",
|
||||
"party": "Konfetti",
|
||||
"pip": "Bild-in-Bild-Modus ein-/ausschalten",
|
||||
"privateMessage": "Private Nachricht senden",
|
||||
"profile": "Profil bearbeiten",
|
||||
"raiseHand": "Hand erheben / senken",
|
||||
"reactionsMenu": "Interaktionsmenü öffnen / schließen",
|
||||
"recording": "Aufzeichnung ein-/ausschalten",
|
||||
"remoteMute": "Personen stummschalten",
|
||||
"remoteVideoMute": "Kamera von dieser Person ausschalten",
|
||||
@@ -810,23 +870,29 @@
|
||||
"shortcuts": "Tastenkombinationen ein-/ausblenden",
|
||||
"show": "Im Vordergrund anzeigen",
|
||||
"speakerStats": "Sprechstatistik ein-/ausblenden",
|
||||
"surprised": "Überrascht",
|
||||
"tileView": "Kachelansicht ein-/ausschalten",
|
||||
"toggleCamera": "Kamera wechseln",
|
||||
"toggleFilmstrip": "Miniaturansichten ein-/ausschalten",
|
||||
"videomute": "„Video stummschalten“ ein-/ausschalten",
|
||||
"videoblur": "Unscharfer Hintergrund ein-/ausschalten",
|
||||
"selectBackground": "Hintergrund auswählen",
|
||||
"expand": "Ausklappen",
|
||||
"collapse": "Einklappen"
|
||||
},
|
||||
"addPeople": "Personen zur Konferenz hinzufügen",
|
||||
"audioSettings": "Ton-Einstellungen",
|
||||
"videoSettings": "Kameraeinstellungen",
|
||||
"audioOnlyOff": "Modus „Nur Audio“ deaktivieren",
|
||||
"audioOnlyOn": "Modus „Nur Audio“ aktivieren",
|
||||
"audioRoute": "Audiogerät auswählen",
|
||||
"authenticate": "Anmelden",
|
||||
"boo": "Buhen",
|
||||
"callQuality": "Qualitätseinstellungen",
|
||||
"chat": "Chat öffnen / schließen",
|
||||
"clap": "Klatschen",
|
||||
"closeChat": "Chat schließen",
|
||||
"closeReactionsMenu": "Interationsmenü schließen",
|
||||
"documentClose": "Geteiltes Dokument schließen",
|
||||
"documentOpen": "Geteiltes Dokument öffnen",
|
||||
"download": "Unsere Apps herunterladen",
|
||||
@@ -840,6 +906,8 @@
|
||||
"hangup": "Konferenz verlassen",
|
||||
"help": "Hilfe",
|
||||
"invite": "Personen einladen",
|
||||
"laugh": "Lachen",
|
||||
"like": "Daumen hoch",
|
||||
"lobbyButtonDisable": "Lobbymodus deaktivieren",
|
||||
"lobbyButtonEnable": "Lobbymodus aktivieren",
|
||||
"login": "Anmelden",
|
||||
@@ -858,12 +926,20 @@
|
||||
"noisyAudioInputTitle": "Ihr Mikrofon scheint lärmintensiv zu sein!",
|
||||
"noisyAudioInputDesc": "Es klingt, als ob Ihr Mikrofon Störgeräusche verursacht. Bitte überlegen Sie, ob Sie das Gerät stummschalten oder austauschen wollen.",
|
||||
"openChat": "Chat öffnen",
|
||||
"openReactionsMenu": "Interationsmenü öffnen",
|
||||
"participants": "Anwesende",
|
||||
"party": "Konfetti",
|
||||
"pip": "Bild-in-Bild-Modus einschalten",
|
||||
"privateMessage": "Private Nachricht senden",
|
||||
"profile": "Profil bearbeiten",
|
||||
"raiseHand": "Hand erheben / senken",
|
||||
"raiseYourHand": "Melden",
|
||||
"reactionBoo": "Buhen senden",
|
||||
"reactionClap": "Klatschen senden",
|
||||
"reactionLaugh": "Lachen senden",
|
||||
"reactionLike": "Daumen hoch senden",
|
||||
"reactionParty": "Konfetti senden",
|
||||
"reactionSurprised": "Überrascht senden",
|
||||
"security": "Sicherheitsoptionen",
|
||||
"Settings": "Einstellungen",
|
||||
"shareaudio": "Audio teilen",
|
||||
@@ -873,14 +949,15 @@
|
||||
"speakerStats": "Sprechstatistik",
|
||||
"startScreenSharing": "Bildschirmfreigabe starten",
|
||||
"startSubtitles": "Untertitel einschalten",
|
||||
"stopAudioSharing": "Audiofreigabe stoppen",
|
||||
"stopScreenSharing": "Bildschirmfreigabe stoppen",
|
||||
"stopSubtitles": "Untertitel ausschalten",
|
||||
"stopSharedVideo": "YouTube-Video stoppen",
|
||||
"surprised": "Überrascht",
|
||||
"talkWhileMutedPopup": "Versuchen Sie zu sprechen? Ihr Mikrofon ist stummgeschaltet.",
|
||||
"tileViewToggle": "Kachelansicht ein-/ausschalten",
|
||||
"toggleCamera": "Kamera wechseln",
|
||||
"videomute": "Kamera starten / stoppen",
|
||||
"videoSettings": "Video-Einstellungen",
|
||||
"selectBackground": "Hintergrund auswählen"
|
||||
},
|
||||
"transcribing": {
|
||||
@@ -974,10 +1051,10 @@
|
||||
"info": "Einwahlinformationen",
|
||||
"join": "ERSTELLEN / BEITRETEN",
|
||||
"jitsiOnMobile": "Jitsi unterwegs – einfach unsere Apps herunterladen und Meetings von überall starten",
|
||||
"moderatedMessage": "Oder <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">reservieren Sie sich eine Konferenz-URL</a>, die nur Sie moderieren.",
|
||||
"mobileDownLoadLinkIos": "iOS App Download",
|
||||
"mobileDownLoadLinkAndroid": "Android App Download",
|
||||
"mobileDownLoadLinkFDroid": "F-Droid App Download",
|
||||
"moderatedMessage": "Oder <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">reservieren Sie sich eine Konferenz-URL</a>, die nur Sie moderieren.",
|
||||
"privacy": "Datenschutz",
|
||||
"recentList": "Verlauf",
|
||||
"recentListDelete": "Eintrag löschen",
|
||||
@@ -1008,6 +1085,7 @@
|
||||
},
|
||||
"lobby": {
|
||||
"admit": "Zulassen",
|
||||
"admitAll": "Alle zulassen",
|
||||
"knockingParticipantList": "Liste anklopfender Personen",
|
||||
"allow": "Annehmen",
|
||||
"backToKnockModeButton": "Kein Passwort, stattdessen Beitritt anfragen",
|
||||
@@ -1038,6 +1116,7 @@
|
||||
"passwordField": "Konferenzpasswort eingeben",
|
||||
"passwordJoinButton": "Beitreten",
|
||||
"reject": "Ablehnen",
|
||||
"rejectAll": "Alle ablehnen",
|
||||
"toggleLabel": "Lobby aktivieren"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,22 +1,33 @@
|
||||
{
|
||||
"addPeople": {
|
||||
"add": "Convidar",
|
||||
"countryNotSupported": "Ainda não suportamos este destino.",
|
||||
"countryReminder": "Está a ligar de fora dos EUA? Por favor, certifique-se de começar com o código do país!",
|
||||
"disabled": "Você não pode convidar pessoas.",
|
||||
"addContacts": "Convidar os seus contactos",
|
||||
"copyInvite": "Cópia do convite para reunião",
|
||||
"copyLink": "Cópia do link da reunião",
|
||||
"copyStream": "Copiar do link de transmissão em direto",
|
||||
"contacts": "contactos",
|
||||
"countryNotSupported": "Ainda não temos suporte para este destino.",
|
||||
"countryReminder": "Está a telefonar para fora dos EUA? Por favor, certifique-se de que começa com o código do país!",
|
||||
"defaultEmail": "O seu e-mail predefinido",
|
||||
"disabled": "Não pode convidar outras pessoas.",
|
||||
"failedToAdd": "Falha ao adicionar participantes",
|
||||
"footerText": "Digitação está desativada.",
|
||||
"inviteMorePrompt": "Convide mais pessoas",
|
||||
"loading": "A procurar por pessoas e números de telefone",
|
||||
"loadingNumber": "A validar o número de telefone",
|
||||
"loadingPeople": "A procurar pessoas para convidar",
|
||||
"noResults": "Nenhum resultado de busca correspondente",
|
||||
"noValidNumbers": "Por favor, digite um número de telefone",
|
||||
"searchNumbers": "Adicionar números de telefone",
|
||||
"searchPeople": "Pesquisar pessoas",
|
||||
"searchPeopleAndNumbers": "Pesquisar por pessoas ou adicionar os seus números de telefone",
|
||||
"footerText": "A marcação está desactivada.",
|
||||
"googleEmail": "E-mail do Google",
|
||||
"inviteMoreHeader": "Você é o único na reunião",
|
||||
"inviteMoreMailSubject": "Participar na reunião {{appName}}",
|
||||
"inviteMorePrompt": "Convidar mais pessoas",
|
||||
"linkCopied": "Link copiado para a área de transferência",
|
||||
"noResults": "Sem resultados de pesquisa correspondentes",
|
||||
"outlookEmail": "E-mail do Outlook",
|
||||
"phoneNumbers": "números de telefone",
|
||||
"searching": "A pesquisar...",
|
||||
"shareInvite": "Partilhar convite de reunião",
|
||||
"shareLink": "Partilhar o link da reunião para convidar outras pessoas",
|
||||
"shareStream": "Partilhar o link de transmissão em direto",
|
||||
"sipAddresses": "endereços SIP",
|
||||
"telephone": "Telefone: {{number}}",
|
||||
"title": "Convide pessoas para sua reunião"
|
||||
"title": "Convidar pessoas para esta reunião",
|
||||
"yahooEmail": "E-mail do Yahoo"
|
||||
},
|
||||
"audioDevices": {
|
||||
"bluetooth": "Bluetooth",
|
||||
@@ -171,7 +182,7 @@
|
||||
"dismiss": "Dispensar",
|
||||
"displayNameRequired": "Olá! Qual é o seu nome?",
|
||||
"done": "Feito",
|
||||
"e2eeDescription": "A encriptação de ponta-a-ponta é actualmente EXPERIMENTAL. Tenha em mente que ligar a encriptação de ponta a ponta irá efectivamente desactivar os serviços fornecidos do lado do servidor, tais como: gravação, transmissão em directo e participação telefónica. Tenha também em mente que o encontro só funcionará para pessoas que se juntem a partir de browsers com suporte para \"insertable streams\".",
|
||||
"e2eeDescription": "A encriptação de ponta a ponta é actualmente EXPERIMENTAL. Tenha em mente que ligar a encriptação de ponta a ponta irá efectivamente desactivar os serviços fornecidos do lado do servidor, tais como: gravação, transmissão em directo e participação telefónica. Tenha também em mente que o encontro só funcionará para pessoas que se juntem a partir de browsers com suporte para \"insertable streams\".",
|
||||
"e2eeLabel": "Habilitar encriptação de ponta a ponta",
|
||||
"e2eeWarning": "AVISO: Nem todos os participantes neste encontro parecem ter apoio para a encriptação de ponta a ponta. Se o permitir, eles não o poderão ver nem ouvir.",
|
||||
"enterDisplayName": "Digite o seu nome aqui",
|
||||
@@ -271,10 +282,10 @@
|
||||
"shareYourScreen": "Partilhe o seu ecrã",
|
||||
"shareYourScreenDisabled": "Partilha de ecrã desactivada.",
|
||||
"startLiveStreaming": "Iniciar a transmissão em directo",
|
||||
"startRecording": "Iniciar a gravação",
|
||||
"startRecording": "Iniciar gravação",
|
||||
"startRemoteControlErrorMessage": "Ocorreu um erro ao tentar iniciar a sessão de controlo remoto!",
|
||||
"stopLiveStreaming": "Parar a transmissão em direto",
|
||||
"stopRecording": "Parar a gravação",
|
||||
"stopRecording": "Parar gravação",
|
||||
"stopRecordingWarning": "Tem a certeza de que gostaria de parar a gravação?",
|
||||
"stopStreamingWarning": "Tem a certeza de que gostaria de parar a transmissão em direto?",
|
||||
"streamKey": "Chave de transmissão em direto",
|
||||
@@ -368,7 +379,7 @@
|
||||
"focusRemote": "Focar no vídeo de outro participante",
|
||||
"fullScreen": "Entrar ou sair da tela cheia",
|
||||
"keyboardShortcuts": "Atalhos de teclado",
|
||||
"localRecording": "Mostrar ou ocultar controles de gravação local",
|
||||
"localRecording": "Mostrar ou ocultar controlos de gravação local",
|
||||
"mute": "Deixar mudo ou não o microfone",
|
||||
"pushToTalk": "Pressione para falar",
|
||||
"raiseHand": "Erga ou baixe sua mão",
|
||||
@@ -416,7 +427,7 @@
|
||||
"on": "Ligado",
|
||||
"unknown": "Desconhecido"
|
||||
},
|
||||
"dialogTitle": "Controles da Gravação Local",
|
||||
"dialogTitle": "Controlos da Gravação Local",
|
||||
"duration": "Duração",
|
||||
"durationNA": "N/D",
|
||||
"encoding": "Codificando",
|
||||
@@ -436,7 +447,7 @@
|
||||
"participantStats": "Estatísticas dos Participantes",
|
||||
"sessionToken": "Token de Sessão",
|
||||
"start": "Iniciar gravação",
|
||||
"stop": "Parar a Gravação",
|
||||
"stop": "Parar gravação",
|
||||
"yes": "Sim"
|
||||
},
|
||||
"lockRoomPassword": "senha",
|
||||
@@ -481,10 +492,62 @@
|
||||
"passwordDigitsOnly": "Até {{number}} dígitos",
|
||||
"poweredby": "distribuído por",
|
||||
"prejoin": {
|
||||
"doNotShow": "Não mostre este ecrã novamente",
|
||||
"joinMeeting": "Participar na reunião",
|
||||
"joinWithoutAudio": "Participar sem áudio",
|
||||
"lookGood": "O seu microfone está a funcionar corretamente"
|
||||
"audioAndVideoError": "Erro no áudio e vídeo:",
|
||||
"audioDeviceProblem": "Há um problema com o seu dispositivo de áudio",
|
||||
"audioOnlyError": "Erro no áudio:",
|
||||
"audioTrackError": "Não foi possível criar a pista de áudio.",
|
||||
"calling": "A chamar",
|
||||
"callMe": "Ligue-me",
|
||||
"callMeAtNumber": "Ligue-me para este número:",
|
||||
"configuringDevices": "A configurar os dispositivos...",
|
||||
"connectedWithAudioQ": "Está ligado com áudio?",
|
||||
"connection": {
|
||||
"good": "A sua ligação à Internet parece boa!",
|
||||
"nonOptimal": "A sua ligação à Internet não é óptima",
|
||||
"poor": "Tem uma má ligação à Internet"
|
||||
},
|
||||
"connectionDetails": {
|
||||
"audioClipping": "Prevemos que o seu áudio tenha cortes.",
|
||||
"audioHighQuality": "Prevemos que o seu áudio tenha excelente qualidade.",
|
||||
"audioLowNoVideo": "Prevemos que a qualidade do seu áudio seja baixa e sem vídeo.",
|
||||
"goodQuality": "Fantástico! A qualidade dos seus meios de comunicação vai ser óptima.",
|
||||
"noMediaConnectivity": "Não foi possível encontrar uma forma de estabelecer a conectividade dos meios de comunicação para este teste. Isto é tipicamente causado por uma firewall ou NAT.",
|
||||
"noVideo": "Prevemos que o seu vídeo seja terrível.",
|
||||
"undetectable": "Se mesmo assim não conseguir fazer chamadas no browser, recomendamos que se certifique de que os seus altifalantes, microfone e câmara estão devidamente configurados, que concedeu ao seu browser direitos de utilização do seu microfone e câmara, e que a versão do seu browser está actualizada. Se mesmo assim tiver problemas em telefonar, deverá contactar o criador da aplicação web.",
|
||||
"veryPoorConnection": "Prevemos que a qualidade da sua chamada seja realmente terrível.",
|
||||
"videoFreezing": "Prevemos que o seu vídeo congele, fique preto, e seja pixelizado.",
|
||||
"videoHighQuality": "Prevemos que o seu vídeo tenha boa qualidade.",
|
||||
"videoLowQuality": "Prevemos que o seu vídeo tenha baixa qualidade em termos de velocidade de fotogramas e resolução.",
|
||||
"videoTearing": "Prevemos que o seu vídeo seja pixelizado ou que tenha artefactos visuais."
|
||||
},
|
||||
"copyAndShare": "Copiar e partilhar a ligação da reunião.",
|
||||
"dialInMeeting": "Entrar com chamada telefónica",
|
||||
"dialInPin": "Entrar com chamada telefónica e introduzir o código PIN:",
|
||||
"dialing": "A marcar",
|
||||
"doNotShow": "Não volte a mostrar este ecrã",
|
||||
"errorDialOut": "Não foi possível marcar",
|
||||
"errorDialOutDisconnected": "Não foi possível marcar. Desligado",
|
||||
"errorDialOutFailed": "Não foi possível marcar. Falha na chamada",
|
||||
"errorDialOutStatus": "Erro ao obter o estado da chamada realizada",
|
||||
"errorMissingName": "Por favor, digite o seu nome para participar na reunião",
|
||||
"errorStatusCode": "Erro ao marcar, código do estado: {{status}}",
|
||||
"errorValidation": "Falhou a validação do número",
|
||||
"iWantToDialIn": "Quero entrar por telefone",
|
||||
"joinAudioByPhone": "Entrar com o áudio do telefone",
|
||||
"joinMeeting": "Entrar na reunião",
|
||||
"joinWithoutAudio": "Entrar sem áudio",
|
||||
"initiated": "Chamada iniciada",
|
||||
"linkCopied": "Ligação copiada para a área de transferência",
|
||||
"lookGood": "Parece que o seu microfone está a funcionar corretamente",
|
||||
"or": "ou",
|
||||
"premeeting": "Pré-reunião",
|
||||
"showScreen": "Ativar o ecrã de pré-reunião",
|
||||
"keyboardShortcuts" : "Ativar os atalhos de teclado",
|
||||
"startWithPhone": "Iniciar com o áudio do telefone",
|
||||
"screenSharingError": "Erro de partilha de ecrã:",
|
||||
"videoOnlyError": "Erro de vídeo:",
|
||||
"videoTrackError": "Não foi possível criar a pista de vídeo.",
|
||||
"viewAllNumbers": "ver todos os números"
|
||||
},
|
||||
"presenceStatus": {
|
||||
"busy": "Ocupado",
|
||||
@@ -539,26 +602,39 @@
|
||||
},
|
||||
"settings": {
|
||||
"calendar": {
|
||||
"about": "A integração do calendário {{appName}} é usada para acessar com segurança o seu calendário para que ele possa ler os próximos eventos.",
|
||||
"disconnect": "Desconectar",
|
||||
"microsoftSignIn": "Entrar com Microsoft",
|
||||
"signedIn": "Atualmente acessando eventos do calendário para {{email}}. Clique no botão Desconectar abaixo para parar de acessar os eventos da agenda.",
|
||||
"about": "A integração do calendário {{appName}} é utilizada para aceder com segurança ao seu calendário para que este possa ler os próximos eventos.",
|
||||
"disconnect": "Desligar",
|
||||
"microsoftSignIn": "Iniciar sessão com a Microsoft",
|
||||
"signedIn": "Atualmente a aceder a eventos de calendário por {{email}}. Clique no botão Desconectar abaixo para parar de aceder a eventos de calendário.",
|
||||
"title": "Calendário"
|
||||
},
|
||||
"desktopShareFramerate": "Taxa de fotogramas para partilha do ambiente de trabalho",
|
||||
"desktopShareWarning": "É necessário reiniciar a partilha do ecrã para que as novas definições entrem em vigor.",
|
||||
"desktopShareHighFpsWarning": "Uma taxa de fotogramas mais elevada para a partilha do ambiente de trabalho pode afectar a sua largura de banda. É necessário reiniciar a partilha de ecrã para que as novas definições entrem em vigor.",
|
||||
"devices": "Dispositivos",
|
||||
"followMe": "Todos me seguem",
|
||||
"framesPerSecond": "fotogramas-por-segundo",
|
||||
"incomingMessage": "Receber mensagem",
|
||||
"language": "Idioma",
|
||||
"loggedIn": "Conectado como {{name}}",
|
||||
"loggedIn": "Sessão iniciada como {{name}}",
|
||||
"microphones": "Microfones",
|
||||
"moderator": "Moderador",
|
||||
"more": "Mais",
|
||||
"name": "Nome",
|
||||
"noDevice": "Nenhum",
|
||||
"participantJoined": "Entrar participante",
|
||||
"participantLeft": "Sair participante",
|
||||
"playSounds": "Reproduzir som quando",
|
||||
"sameAsSystem": "O mesmo que o sistema ({{label}})",
|
||||
"selectAudioOutput": "Saída de áudio",
|
||||
"selectCamera": "Câmera",
|
||||
"selectCamera": "Câmara",
|
||||
"selectMic": "Microfone",
|
||||
"startAudioMuted": "Todos iniciam mudos",
|
||||
"startVideoMuted": "Todos iniciam ocultos",
|
||||
"title": "Configurações"
|
||||
"sounds": "Sons",
|
||||
"speakers": "Participantes",
|
||||
"startAudioMuted": "Todos começam com microfone desligado",
|
||||
"startVideoMuted": "Todos começam com câmara desligada",
|
||||
"talkWhileMuted": "se fala e está com microfone desligado",
|
||||
"title": "Definições"
|
||||
},
|
||||
"settingsView": {
|
||||
"advanced": "",
|
||||
@@ -643,15 +719,15 @@
|
||||
"sharedvideo": "Mudar a partilha de vídeos do YouTube",
|
||||
"shareRoom": "Convidar alguém",
|
||||
"shareYourScreen": "Iniciar / Parar de partilhar o seu ecrã",
|
||||
"shortcuts": "Mudar atalhos",
|
||||
"shortcuts": "Mostrar / Esconder atalhos",
|
||||
"show": "Mostrar no palco",
|
||||
"speakerStats": "Mudar estatísticas do apresentador",
|
||||
"speakerStats": "Mostrar / Esconder estatísticas dos participantes",
|
||||
"tileView": "Mudar para vista em quadrícula",
|
||||
"toggleCamera": "Mudar a câmara",
|
||||
"toggleFilmstrip": "Mudar para película de filme",
|
||||
"videomute": "Iniciar / Parar câmara",
|
||||
"videoblur": "Mudar o desfoque de vídeo",
|
||||
"selectBackground": "Selecionar o fundo",
|
||||
"selectBackground": "Selecionar plano de fundo",
|
||||
"expand": "Expandir",
|
||||
"collapse": "Colapsar"
|
||||
},
|
||||
@@ -708,7 +784,7 @@
|
||||
"sharedvideo": "Partilhar vídeo",
|
||||
"shareRoom": "Convidar alguém",
|
||||
"shortcuts": "Ver atalhos",
|
||||
"speakerStats": "Estatísticas do Apresentador",
|
||||
"speakerStats": "Estatísticas dos participantes",
|
||||
"startScreenSharing": "Iniciar partilha de ecrã",
|
||||
"startSubtitles": "Iniciar legendas",
|
||||
"stopScreenSharing": "Parar partilha de ecrã",
|
||||
@@ -718,7 +794,7 @@
|
||||
"tileViewToggle": "Mudar para vista em quadrícula",
|
||||
"toggleCamera": "Mudar a câmara",
|
||||
"videomute": "Iniciar / Parar câmara",
|
||||
"selectBackground": "Selecionar o fundo"
|
||||
"selectBackground": "Selecionar plano de fundo"
|
||||
},
|
||||
"transcribing": {
|
||||
"ccButtonTooltip": "Iniciar/parar legendas",
|
||||
|
||||
@@ -70,12 +70,17 @@
|
||||
},
|
||||
"privateNotice": "私人訊息傳送至 {{recipient}}",
|
||||
"title": "對話",
|
||||
"you": "您"
|
||||
"you": "您",
|
||||
"message": "訊息",
|
||||
"messageAccessibleTitle": "{{user}} 說:",
|
||||
"messageAccessibleTitleMe": "您說:",
|
||||
"smileysPanel": "表情符號面板"
|
||||
},
|
||||
"chromeExtensionBanner": {
|
||||
"installExtensionText": "安裝適用於 Google 行事曆及 Office 365 整合的擴充功能",
|
||||
"buttonText": "安裝 Chrome 擴充功能",
|
||||
"dontShowAgain": "不再顯示"
|
||||
"dontShowAgain": "不再顯示",
|
||||
"close": "關閉"
|
||||
},
|
||||
"connectingOverlay": {
|
||||
"joiningRoom": "正在將您連接至您的會議..."
|
||||
@@ -204,10 +209,13 @@
|
||||
"e2eeLabel": "啟用端對端加密",
|
||||
"e2eeWarning": "警告:看來不是每位此會議的參與者都有啟用端對端加密,如果您啟用了,他們可能無法看/聽到您。",
|
||||
"enterDisplayName": "請在此輸入您自己的名字",
|
||||
"enterDisplayNameToJoin": "請輸入您的名字以加入",
|
||||
"embedMeeting": "嵌入會議",
|
||||
"error": "錯誤",
|
||||
"gracefulShutdown": "我們的服務目前關閉維護中,請稍後再試。",
|
||||
"grantModeratorDialog": "您確定要使此參與者成為管理員嗎?",
|
||||
"grantModeratorTitle": "授予管理員",
|
||||
"hideShareAudioHelper": "不再顯示",
|
||||
"IamHost": "我是主辦人",
|
||||
"incorrectRoomLockPassword": "密碼不符",
|
||||
"incorrectPassword": "錯誤的用戶名稱或密碼",
|
||||
@@ -260,11 +268,14 @@
|
||||
"passwordNotSupported": "尚未支援設定會議密碼 $t(lockRoomPassword)。",
|
||||
"passwordNotSupportedTitle": "尚未支援 $t(lockRoomPasswordUppercase)",
|
||||
"passwordRequired": "必須要有 $t(lockRoomPasswordUppercase)",
|
||||
"permissionErrorTitle": "需要權限",
|
||||
"permissionCameraRequiredError": "參與視訊會議需要存取攝影機的權限。請在設定中授權",
|
||||
"permissionMicRequiredError": "參與音訊會議需要存取麥克風的權限。請在設定中授權",
|
||||
"popupError": "您的瀏覽器在此網站上阻擋彈出視窗。請在瀏覽器的安全設定中啟用並再試一次。",
|
||||
"popupErrorTitle": "彈出視窗遭到阻擋",
|
||||
"readMore": "閱讀更多",
|
||||
"readMore": "更多",
|
||||
"recording": "錄影中",
|
||||
"recordingDisabledForGuestTooltip": "訪客無法啟動錄影。",
|
||||
"recordingDisabledBecauseOfActiveLiveStreamingTooltip": "正在直播時無法使用",
|
||||
"recordingDisabledTooltip": "啟動錄影已停用。",
|
||||
"rejoinNow": "立即重新加入",
|
||||
"remoteControlAllowedMessage": "{{user}} 接受您進行遠端控制的請求!",
|
||||
@@ -293,6 +304,13 @@
|
||||
"sessTerminated": "通話已經終止",
|
||||
"sessionRestarted": "通話被橋接器重新啟動",
|
||||
"Share": "分享",
|
||||
"shareAudio": "繼續",
|
||||
"shareAudioTitle" : "如何分享音訊",
|
||||
"shareAudioWarningTitle": "您必須先停止分享畫面才能分享音訊",
|
||||
"shareAudioWarningH1": "如果您只要分享音訊:",
|
||||
"shareAudioWarningD1": "您必須先停止分享畫面才能分享音訊。",
|
||||
"shareAudioWarningD2": "您必須重新啟動畫面分享並勾選 \"分享音訊\" 選項。",
|
||||
"shareMediaWarningGenericH2": "如果您想要同時分享畫面跟音訊",
|
||||
"shareVideoLinkError": "請提供正確的 YouTube 連結。",
|
||||
"shareVideoTitle": "分享影像",
|
||||
"shareYourScreen": "分享自己的螢幕",
|
||||
@@ -300,6 +318,10 @@
|
||||
"startLiveStreaming": "啟動直播串流",
|
||||
"startRecording": "啟動錄影作業",
|
||||
"startRemoteControlErrorMessage": "嘗試啟動遠端控制階段時發生錯誤!",
|
||||
"shareScreenWarningTitle": "您必須先停止分享音訊才能分享畫面",
|
||||
"shareScreenWarningH1": "如果您只要分享畫面:",
|
||||
"shareScreenWarningD1": "您必須先停止分享音訊才能分享畫面。",
|
||||
"shareScreenWarningD2": "您必須先停止分享音訊,啟動畫面分享,然後勾選 \"分享音訊\" 選項。",
|
||||
"stopLiveStreaming": "停止直播串流",
|
||||
"stopRecording": "停止錄影",
|
||||
"stopRecordingWarning": "確定要停止錄影嗎?",
|
||||
@@ -316,11 +338,16 @@
|
||||
"userIdentifier": "使用者標識符",
|
||||
"userPassword": "用戶密碼",
|
||||
"videoLink": "影片連結",
|
||||
"viewUpgradeOptions": "查看升級方案",
|
||||
"viewUpgradeOptionsContent": "若要無限制地使用進階版功能,像是錄影、逐字稿、RTMP 串流...等,您必須升級您的方案。",
|
||||
"viewUpgradeOptionsTitle": "您找到了進階版功能!",
|
||||
"WaitForHostMsg": "此會議 <b>{{room}}</b> 尚未啟動。如果您是會議主人,請進行認證;否則,請等待會議主人到達。",
|
||||
"WaitForHostMsgWOk": "此會議 <b>{{room}}</b> 尚未啟動。如果您是會議主人,請按 [確定] 進行認證;否則,請等待會議主人到達。",
|
||||
"WaitingForHost": "等侯主辦人...",
|
||||
"Yes": "是的",
|
||||
"yourEntireScreen": "您的畫面"
|
||||
"yourEntireScreen": "您的畫面",
|
||||
"remoteUserControls": "{{username}} 的遠端使用者控制",
|
||||
"localUserControls": "本機使用者控制"
|
||||
},
|
||||
"dialOut": {
|
||||
"statusMessage": "現在狀態為 {{status}}"
|
||||
@@ -341,8 +368,20 @@
|
||||
"slightBlur": "稍微模糊",
|
||||
"removeBackground": "移除背景",
|
||||
"addBackground": "新增背景",
|
||||
"pleaseWait": "請稍候...",
|
||||
"none": "無",
|
||||
"desktopShareError": "無法建立桌面分享"
|
||||
"uploadedImage": "上傳圖片 {{index}}",
|
||||
"deleteImage": "刪除圖片",
|
||||
"image1" : "海灘",
|
||||
"image2" : "牆壁 (白)",
|
||||
"image3" : "空房間 (白)",
|
||||
"image4" : "落地燈 (黑)",
|
||||
"image5" : "山岳",
|
||||
"image6" : "森林",
|
||||
"image7" : "日出",
|
||||
"desktopShareError": "無法建立桌面分享",
|
||||
"desktopShare":"桌面分享",
|
||||
"webAssemblyWarning": "不支援 WebAssembly"
|
||||
},
|
||||
"feedback": {
|
||||
"average": "普通",
|
||||
@@ -351,7 +390,8 @@
|
||||
"good": "很好",
|
||||
"rateExperience": "對本次會議的體驗評分",
|
||||
"veryBad": "極差",
|
||||
"veryGood": "極好"
|
||||
"veryGood": "極好",
|
||||
"star": "星級"
|
||||
},
|
||||
"incomingCall": {
|
||||
"answer": "接通",
|
||||
@@ -366,8 +406,9 @@
|
||||
"cancelPassword": "取消 $t(lockRoomPassword)",
|
||||
"conferenceURL": "連結:",
|
||||
"country": "國家",
|
||||
"dialANumber": "要參加您的會議,撥打以下號碼其中之一,然後輸入 PIN 碼。",
|
||||
"dialANumber": "要參加您的會議,撥打以下其中一支號碼,然後輸入 PIN 碼。",
|
||||
"dialInConferenceID": "PIN 號碼:",
|
||||
"copyNumber":"複製號碼",
|
||||
"dialInNotSupported": "抱歉,目前不支援電話撥入。",
|
||||
"dialInNumber": "撥入:",
|
||||
"dialInSummaryError": "目前解析撥入資訊錯誤。請稍後再試一次。",
|
||||
@@ -387,6 +428,7 @@
|
||||
"noRoom": "沒有會議室指定要撥入。",
|
||||
"numbers": "撥入號碼",
|
||||
"password": "$t(lockRoomPasswordUppercase):",
|
||||
"sip": "SIP 位址",
|
||||
"title": "分享",
|
||||
"tooltip": "顯示此會議的連結及電話撥入號碼",
|
||||
"label": "撥入資訊"
|
||||
@@ -405,6 +447,7 @@
|
||||
"support": "支援",
|
||||
"supportMsg": "如果這種狀況持續發生,請聯絡"
|
||||
},
|
||||
"jitsiHome": "{{logo}} 商標,首頁連結",
|
||||
"keyboardShortcuts": {
|
||||
"focusLocal": "聚焦於自己的影像",
|
||||
"focusRemote": "聚焦於另一人的影像",
|
||||
@@ -495,7 +538,8 @@
|
||||
"disconnected": "已經中斷連接",
|
||||
"focus": "會議焦點",
|
||||
"focusFail": "{{component}} 無法使用 - 請在 {{ms}} 秒後重試",
|
||||
"grantedTo": "主持人權限已授予 {{to}} !",
|
||||
"grantedTo": "主持人權限已授予 {{to}}!",
|
||||
"hostAskedUnmute": "主持人希望您能解除靜音",
|
||||
"invitedOneMember": "{{name}} 已受邀請",
|
||||
"invitedThreePlusMembers": "{{name}} 及 {{count}} 位人員已受邀請",
|
||||
"invitedTwoMembers": "{{first}} 及 {{second}} 已受邀請",
|
||||
@@ -516,7 +560,7 @@
|
||||
"somebody": "某人",
|
||||
"startSilentTitle": "您加入了會議而無聲音輸出!",
|
||||
"startSilentDescription": "重新加入會議以啟用語音",
|
||||
"suboptimalBrowserWarning": "恐怕您本次會議體驗並不太好,我們會想辦法改進的。但在此之前,敬請使用 <a href='{{recommendedBrowserPageLink}}' target='_blank'>完全支援的瀏覽器</a> 。",
|
||||
"suboptimalBrowserWarning": "恐怕您本次會議體驗並不太好,我們會想辦法改進的。但在此之前,敬請使用<a href='{{recommendedBrowserPageLink}}' target='_blank'>完全支援的瀏覽器</a> 。",
|
||||
"suboptimalExperienceTitle": "瀏覽器警告",
|
||||
"unmute": "取消靜音",
|
||||
"newDeviceCameraTitle": "偵測到新的攝影裝置",
|
||||
@@ -525,16 +569,34 @@
|
||||
"OldElectronAPPTitle": "安全漏洞!",
|
||||
"oldElectronClientDescription1": "您似乎正在使用 Jitsi Meet 客戶端的舊版本,其有已知的安全漏洞。請更新到",
|
||||
"oldElectronClientDescription2": "最新版本",
|
||||
"oldElectronClientDescription3": "!"
|
||||
"oldElectronClientDescription3": "!",
|
||||
"moderationInEffectDescription": "若要說話,請舉手",
|
||||
"moderationInEffectCSDescription": "若要分享視訊,請舉手",
|
||||
"moderationInEffectVideoDescription": "若要使視訊可見,請舉手",
|
||||
"moderationInEffectTitle": "麥克風已被管理員靜音",
|
||||
"moderationInEffectCSTitle": "內容分享已被管理員停用",
|
||||
"moderationInEffectVideoTitle": "視訊已被管理員靜音",
|
||||
"moderationRequestFromModerator": "主持人希望您能解除靜音",
|
||||
"moderationRequestFromParticipant": "想要講話",
|
||||
"moderationStartedTitle": "開始管理",
|
||||
"moderationStoppedTitle": "停止管理",
|
||||
"moderationToggleDescription": "由 {{participantDisplayName}}",
|
||||
"raiseHandAction": "舉手",
|
||||
"groupTitle": "通知"
|
||||
},
|
||||
"participantsPane": {
|
||||
"close": "關閉",
|
||||
"headings": {
|
||||
"lobby": "大廳 ({{count}})",
|
||||
"participantsList": "會議參與者 ({{count}})"
|
||||
},
|
||||
"actions": {
|
||||
"allow": "允許參與者能夠:",
|
||||
"invite": "邀請他人",
|
||||
"askUnmute": "要求解除靜音",
|
||||
"muteAll": "靜音所有人",
|
||||
"startModeration": "將他們解除靜音或開始視訊",
|
||||
"stopEveryonesVideo": "停止所有人的視訊",
|
||||
"stopVideo": "停止影片"
|
||||
}
|
||||
},
|
||||
@@ -592,6 +654,7 @@
|
||||
"or": "或",
|
||||
"premeeting": "會前",
|
||||
"showScreen": "啟用會前畫面",
|
||||
"keyboardShortcuts" : "啟用鍵盤快捷鍵",
|
||||
"startWithPhone": "使用手機音訊裝置開始",
|
||||
"screenSharingError": "畫面分享錯誤:",
|
||||
"videoOnlyError": "視訊錯誤:",
|
||||
@@ -613,6 +676,7 @@
|
||||
"ringing": "鈴鈴鈴..."
|
||||
},
|
||||
"profile": {
|
||||
"avatar": "頭像",
|
||||
"setDisplayNameLabel": "設定您的顯示名稱",
|
||||
"setEmailInput": "輸入您的電子信箱",
|
||||
"setEmailLabel": "設定您的 Gravatar 電子信箱",
|
||||
@@ -627,12 +691,15 @@
|
||||
"beta": "BETA",
|
||||
"busy": "我們正在釋放錄影資源。請過幾分鐘後再試。",
|
||||
"busyTitle": "全部錄影目前忙碌",
|
||||
"copyLink": "複製連結",
|
||||
"error": "錄影失敗。請重試。",
|
||||
"errorFetchingLink": "取得錄影檔連結時發生錯誤。",
|
||||
"expandedOff": "錄影已經停止",
|
||||
"expandedOn": "此會議目前正在錄影。",
|
||||
"expandedPending": "錄影正在啟動...",
|
||||
"failedToStart": "錄影啟動失敗",
|
||||
"fileSharingdescription": "分享錄影給會議參與者",
|
||||
"linkGenerated": "我們建立了您的錄影檔的連結。",
|
||||
"live": "直播",
|
||||
"loggedIn": "以 {{userName}} 登入",
|
||||
"off": "錄影已經停止",
|
||||
@@ -647,7 +714,8 @@
|
||||
"signIn": "登入",
|
||||
"signOut": "登出",
|
||||
"unavailable": "喔哦!{{serviceName}} 目前無法使用。我們正在解決此問題,請稍後再試。",
|
||||
"unavailableTitle": "錄影無法使用"
|
||||
"unavailableTitle": "錄影無法使用",
|
||||
"uploadToCloud": "上傳至雲端"
|
||||
},
|
||||
"sectionList": {
|
||||
"pullToRefresh": "拉下以重新整理"
|
||||
@@ -666,8 +734,12 @@
|
||||
"signedIn": "目前能夠存取 {{email}} 的行事曆事件。點按下方取消連接鈕可以停止存取行事曆事件。",
|
||||
"title": "行事曆"
|
||||
},
|
||||
"desktopShareFramerate": "桌面分享幀數",
|
||||
"desktopShareWarning": "您必須重新啟動桌面分享以套用新的設定。",
|
||||
"desktopShareHighFpsWarning": "較高的桌面分享幀數可能會影響您的頻寬。您必須重新啟動桌面分享以套用新的設定。",
|
||||
"devices": "裝置",
|
||||
"followMe": "全部人跟隨我",
|
||||
"framesPerSecond": "幀數",
|
||||
"language": "語言",
|
||||
"loggedIn": "以 {{name}} 登入",
|
||||
"microphones": "麥克風",
|
||||
@@ -736,6 +808,7 @@
|
||||
"callQuality": "管理影像品質",
|
||||
"cc": "切換字幕",
|
||||
"chat": "切換聊天視窗",
|
||||
"clap": "鼓掌",
|
||||
"document": "切換分享文件",
|
||||
"download": "下載我們的應用程式",
|
||||
"embedMeeting": "嵌入會議",
|
||||
@@ -745,7 +818,9 @@
|
||||
"hangup": "離開通話",
|
||||
"help": "說明",
|
||||
"invite": "邀請人員",
|
||||
"joy": "笑到流淚",
|
||||
"kick": "踢出參與者",
|
||||
"like": "比讚",
|
||||
"lobbyButton": "啟用/停用大廳模式",
|
||||
"localRecording": "切換本地端錄影控制",
|
||||
"lockRoom": "切換會議密碼",
|
||||
@@ -758,10 +833,12 @@
|
||||
"muteEveryonesVideo": "停用所有人的攝影機",
|
||||
"muteEveryoneElsesVideo": "停用其他人的攝影機",
|
||||
"participants": "參與者",
|
||||
"party": "拉炮",
|
||||
"pip": "切換子母畫面模式",
|
||||
"privateMessage": "發送私人訊息",
|
||||
"profile": "編輯您的簡介",
|
||||
"raiseHand": "切換舉手",
|
||||
"reactionsMenu": "切換反應選單",
|
||||
"recording": "切換錄影",
|
||||
"remoteMute": "靜音參與者",
|
||||
"remoteVideoMute": "停用參與者的攝影機",
|
||||
@@ -773,22 +850,30 @@
|
||||
"shareYourScreen": "切換螢幕分享",
|
||||
"shortcuts": "切換快捷鍵",
|
||||
"show": "顯示在台上",
|
||||
"smile": "微笑",
|
||||
"speakerStats": "切換聲音輸出統計",
|
||||
"surprised": "驚訝",
|
||||
"tileView": "切換格狀檢視",
|
||||
"toggleCamera": "切換攝影機",
|
||||
"toggleFilmstrip": "切換幻燈片",
|
||||
"videomute": "切換影片靜音",
|
||||
"selectBackground": "選擇背景"
|
||||
"videomute": "啟用/停用攝影機",
|
||||
"videoblur": "切換畫面模糊",
|
||||
"selectBackground": "選擇背景",
|
||||
"expand": "展開",
|
||||
"collapse": "收回"
|
||||
},
|
||||
"addPeople": "新增人員到您的通話中",
|
||||
"audioSettings": "音訊設定",
|
||||
"videoSettings": "視訊設定",
|
||||
"audioOnlyOff": "停用低頻寬模式",
|
||||
"audioOnlyOn": "啟用低頻寬模式",
|
||||
"audioRoute": "選擇音訊裝置",
|
||||
"authenticate": "認證",
|
||||
"callQuality": "管理影像品質",
|
||||
"chat": "開啟/關閉聊天欄",
|
||||
"clap": "鼓掌",
|
||||
"closeChat": "關閉聊天欄",
|
||||
"closeReactionsMenu": "關閉反應選單",
|
||||
"documentClose": "關閉分享檔案欄",
|
||||
"documentOpen": "開啟分享檔案欄",
|
||||
"download": "下載我們的應用程式",
|
||||
@@ -802,6 +887,8 @@
|
||||
"hangup": "離開",
|
||||
"help": "說明",
|
||||
"invite": "邀請人員",
|
||||
"joy": "高興",
|
||||
"like": "比讚",
|
||||
"lobbyButtonDisable": "停用大廳模式",
|
||||
"lobbyButtonEnable": "啟用大廳模式",
|
||||
"login": "登入",
|
||||
@@ -819,30 +906,40 @@
|
||||
"noAudioSignalDialInLinkDesc": "撥入號碼",
|
||||
"noisyAudioInputTitle": "您的麥克風疑似有雜音!",
|
||||
"noisyAudioInputDesc": "噪音聽起來是從您的麥克風傳來的,請考慮靜音或更換裝置。",
|
||||
"openChat": "開啟交談",
|
||||
"openChat": "開啟聊天欄",
|
||||
"openReactionsMenu": "開啟反應選單",
|
||||
"participants": "參與者",
|
||||
"party": "慶祝",
|
||||
"pip": "進入子母畫面模式",
|
||||
"privateMessage": "發送私人訊息",
|
||||
"profile": "編輯您的簡介",
|
||||
"raiseHand": "舉手/取消請求發言",
|
||||
"raiseYourHand": "舉手發言",
|
||||
"reactionClap": "傳送鼓掌反應",
|
||||
"reactionJoy": "傳送高興反應",
|
||||
"reactionLike": "傳送比讚反應",
|
||||
"reactionParty": "傳送拉炮反應",
|
||||
"reactionSmile": "傳送微笑反應",
|
||||
"reactionSurprised": "傳送驚訝反應",
|
||||
"security": "安全性選項",
|
||||
"Settings": "設定",
|
||||
"shareaudio": "分享音訊",
|
||||
"sharedvideo": "分享影片",
|
||||
"shareRoom": "邀請他人",
|
||||
"shortcuts": "查看快捷鍵",
|
||||
"smile": "微笑",
|
||||
"speakerStats": "聲音輸出數據",
|
||||
"startScreenSharing": "啟動螢幕分享",
|
||||
"startScreenSharing": "啟動畫面分享",
|
||||
"startSubtitles": "啟動字幕",
|
||||
"stopScreenSharing": "停止螢幕分享",
|
||||
"stopAudioSharing": "停止分享音訊",
|
||||
"stopScreenSharing": "停止分享畫面",
|
||||
"stopSubtitles": "停止字幕",
|
||||
"stopSharedVideo": "停止影片",
|
||||
"surprised": "驚訝",
|
||||
"talkWhileMutedPopup": "您要發言嗎?目前您處於靜音。",
|
||||
"tileViewToggle": "切換格狀檢視",
|
||||
"toggleCamera": "切換攝影機",
|
||||
"videomute": "啟動/停止攝影機",
|
||||
"videoSettings": "影像設定",
|
||||
"selectBackground": "選擇背景"
|
||||
},
|
||||
"transcribing": {
|
||||
@@ -869,6 +966,7 @@
|
||||
"react-nativeGrantPermissions": "當瀏覽器要求權限時,請選擇<b><i>允許</i></b>。",
|
||||
"safariGrantPermissions": "當瀏覽器要求權限時,請選擇<b><i>確定</i></b>。"
|
||||
},
|
||||
"volumeSlider": "音量滑條",
|
||||
"videoSIPGW": {
|
||||
"busy": "我們正在釋放資源。請過幾分鐘後再試。",
|
||||
"busyTitle": "會議室服務目前忙碌中",
|
||||
@@ -913,6 +1011,7 @@
|
||||
"videomute": "參與者已經停止攝影裝置"
|
||||
},
|
||||
"welcomepage": {
|
||||
"addMeetingName": "新增會議名稱",
|
||||
"accessibilityLabel": {
|
||||
"join": "點擊即可加入",
|
||||
"roomname": "輸入會議室名稱"
|
||||
@@ -934,6 +1033,9 @@
|
||||
"info": "資訊",
|
||||
"join": "建立 / 加入",
|
||||
"jitsiOnMobile": "Jitsi 手機應用程式 – 下載應用程式,不論何時何地都能發起會議",
|
||||
"mobileDownLoadLinkIos": "下載 iOS 版本的手機應用程式",
|
||||
"mobileDownLoadLinkAndroid": "下載 Android 版本的手機應用程式",
|
||||
"mobileDownLoadLinkFDroid": "前往 F-Droid 下載 Android 版本的手機應用程式",
|
||||
"moderatedMessage": "或 <a href=\"{{url}}\" rel=\"noopener noreferrer\" target=\"_blank\">預先建立會議</a>,您將成為唯一一個管理員。",
|
||||
"privacy": "隱私",
|
||||
"recentList": "最近使用",
|
||||
@@ -946,7 +1048,15 @@
|
||||
"sendFeedback": "傳送回饋",
|
||||
"startMeeting": "開始會議",
|
||||
"terms": "條款",
|
||||
"title": "安全、功能齊全、完全免費的視訊會議"
|
||||
"title": "安全、功能齊全、完全免費的視訊會議",
|
||||
"logo":{
|
||||
"calendar":"行事曆圖示",
|
||||
"microsoftLogo":"Microsoft 商標",
|
||||
"logoDeepLinking":"Jitsi meet 商標",
|
||||
"desktopPreviewThumbnail":"桌面分享縮圖",
|
||||
"googleLogo":"Google 商標",
|
||||
"policyLogo":"政策圖示"
|
||||
}
|
||||
},
|
||||
"lonelyMeetingExperience": {
|
||||
"button": "邀請其他人",
|
||||
|
||||
@@ -258,10 +258,12 @@
|
||||
"muteParticipantBody": "You won't be able to unmute them, but they can unmute themselves at any time.",
|
||||
"muteParticipantButton": "Mute",
|
||||
"muteParticipantDialog": "Are you sure you want to mute this participant? You won't be able to unmute them, but they can unmute themselves at any time.",
|
||||
"muteParticipantsVideoDialog": "Are you sure you want to turn off this participant's camera? You won't be able to turn the camera back on, but they can turn it back on at any time.",
|
||||
"muteParticipantTitle": "Mute this participant?",
|
||||
"muteParticipantsVideoButton": "Disable camera",
|
||||
"muteParticipantsVideoTitle": "Disable camera of this participant?",
|
||||
"muteParticipantsVideoBody": "You won't be able to turn the camera back on, but they can turn it back on at any time.",
|
||||
"noDropboxToken": "No valid Dropbox token",
|
||||
"Ok": "OK",
|
||||
"password": "Password",
|
||||
"passwordLabel": "The meeting has been locked by a participant. Please enter the $t(lockRoomPassword) to join.",
|
||||
@@ -418,6 +420,10 @@
|
||||
"invitePhone": "To join by phone instead, tap this: {{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}}",
|
||||
"inviteSipEndpoint": "To join using the SIP address, enter this: {{sipUri}}",
|
||||
"inviteTextiOSPersonal": "{{name}} is inviting you to a meeting.",
|
||||
"inviteTextiOSJoinSilent": "If you are dialing-in through a room phone, use this link to join without connecting to audio: {{silentUrl}}.",
|
||||
"inviteTextiOSInviteUrl": "Click the following link to join: {{inviteUrl}}.",
|
||||
"inviteTextiOSPhone": "To join via phone, use this number: {{number}},,{{conferenceID}}#. If you are looking for a different number, this is the full list: {{didUrl}}.",
|
||||
"inviteURLFirstPartGeneral": "You are invited to join a meeting.",
|
||||
"inviteURLFirstPartPersonal": "{{name}} is inviting you to a meeting.\n",
|
||||
"inviteURLSecondPart": "\nJoin the meeting:\n{{url}}\n",
|
||||
@@ -586,18 +592,24 @@
|
||||
},
|
||||
"participantsPane": {
|
||||
"close": "Close",
|
||||
"header": "Participants",
|
||||
"headings": {
|
||||
"lobby": "Lobby ({{count}})",
|
||||
"participantsList": "Meeting participants ({{count}})"
|
||||
"participantsList": "Meeting participants ({{count}})",
|
||||
"waitingLobby": "Waiting in lobby ({{count}})"
|
||||
},
|
||||
"actions": {
|
||||
"allow": "Allow attendees to:",
|
||||
"blockEveryoneMicCamera": "Block everyone's mic and camera",
|
||||
"invite": "Invite Someone",
|
||||
"askUnmute": "Ask to unmute",
|
||||
"mute": "Mute",
|
||||
"muteAll": "Mute all",
|
||||
"muteEveryoneElse": "Mute everyone else",
|
||||
"startModeration": "Unmute themselves or start video",
|
||||
"stopEveryonesVideo": "Stop everyone's video",
|
||||
"stopVideo": "Stop video"
|
||||
"stopVideo": "Stop video",
|
||||
"unblockEveryoneMicCamera": "Unblock everyone's mic and camera"
|
||||
}
|
||||
},
|
||||
"passwordSetRemotely": "Set by another participant",
|
||||
@@ -740,6 +752,7 @@
|
||||
"devices": "Devices",
|
||||
"followMe": "Everyone follows me",
|
||||
"framesPerSecond": "frames-per-second",
|
||||
"incomingMessage": "Incoming message",
|
||||
"language": "Language",
|
||||
"loggedIn": "Logged in as {{name}}",
|
||||
"microphones": "Microphones",
|
||||
@@ -747,13 +760,18 @@
|
||||
"more": "More",
|
||||
"name": "Name",
|
||||
"noDevice": "None",
|
||||
"participantJoined": "Participant Joined",
|
||||
"participantLeft": "Participant Left",
|
||||
"playSounds": "Play sound on",
|
||||
"sameAsSystem": "Same as system ({{label}})",
|
||||
"selectAudioOutput": "Audio output",
|
||||
"selectCamera": "Camera",
|
||||
"selectMic": "Microphone",
|
||||
"sounds": "Sounds",
|
||||
"speakers": "Speakers",
|
||||
"startAudioMuted": "Everyone starts muted",
|
||||
"startVideoMuted": "Everyone starts hidden",
|
||||
"talkWhileMuted": "Talk while muted",
|
||||
"title": "Settings"
|
||||
},
|
||||
"settingsView": {
|
||||
@@ -805,9 +823,11 @@
|
||||
"accessibilityLabel": {
|
||||
"audioOnly": "Toggle audio only",
|
||||
"audioRoute": "Select the sound device",
|
||||
"boo": "Boo",
|
||||
"callQuality": "Manage video quality",
|
||||
"cc": "Toggle subtitles",
|
||||
"chat": "Open / Close chat",
|
||||
"clap": "Clap",
|
||||
"document": "Toggle shared document",
|
||||
"download": "Download our apps",
|
||||
"embedMeeting": "Embed meeting",
|
||||
@@ -818,6 +838,8 @@
|
||||
"help": "Help",
|
||||
"invite": "Invite people",
|
||||
"kick": "Kick participant",
|
||||
"laugh": "Laugh",
|
||||
"like": "Thumbs Up",
|
||||
"lobbyButton": "Enable/disable lobby mode",
|
||||
"localRecording": "Toggle local recording controls",
|
||||
"lockRoom": "Toggle meeting password",
|
||||
@@ -830,10 +852,12 @@
|
||||
"muteEveryonesVideo": "Disable everyone's camera",
|
||||
"muteEveryoneElsesVideo": "Disable everyone else's camera",
|
||||
"participants": "Participants",
|
||||
"party": "Party Popper",
|
||||
"pip": "Toggle Picture-in-Picture mode",
|
||||
"privateMessage": "Send private message",
|
||||
"profile": "Edit your profile",
|
||||
"raiseHand": "Raise / Lower your hand",
|
||||
"reactionsMenu": "Open / Close reactions menu",
|
||||
"recording": "Toggle recording",
|
||||
"remoteMute": "Mute participant",
|
||||
"remoteVideoMute": "Disable camera of participant",
|
||||
@@ -846,6 +870,7 @@
|
||||
"shortcuts": "Toggle shortcuts",
|
||||
"show": "Show on stage",
|
||||
"speakerStats": "Toggle speaker statistics",
|
||||
"surprised": "Surprised",
|
||||
"tileView": "Toggle tile view",
|
||||
"toggleCamera": "Toggle camera",
|
||||
"toggleFilmstrip": "Toggle filmstrip",
|
||||
@@ -862,9 +887,12 @@
|
||||
"audioOnlyOn": "Enable low bandwidth mode",
|
||||
"audioRoute": "Select the sound device",
|
||||
"authenticate": "Authenticate",
|
||||
"boo": "Boo",
|
||||
"callQuality": "Manage video quality",
|
||||
"chat": "Open / Close chat",
|
||||
"clap": "Clap",
|
||||
"closeChat": "Close chat",
|
||||
"closeReactionsMenu": "Close reactions menu",
|
||||
"documentClose": "Close shared document",
|
||||
"documentOpen": "Open shared document",
|
||||
"download": "Download our apps",
|
||||
@@ -878,6 +906,8 @@
|
||||
"hangup": "Leave the meeting",
|
||||
"help": "Help",
|
||||
"invite": "Invite people",
|
||||
"laugh": "Laugh",
|
||||
"like": "Thumbs Up",
|
||||
"lobbyButtonDisable": "Disable lobby mode",
|
||||
"lobbyButtonEnable": "Enable lobby mode",
|
||||
"login": "Login",
|
||||
@@ -896,12 +926,20 @@
|
||||
"noisyAudioInputTitle": "Your microphone appears to be noisy!",
|
||||
"noisyAudioInputDesc": "It sounds like your microphone is making noise, please consider muting or changing the device.",
|
||||
"openChat": "Open chat",
|
||||
"openReactionsMenu": "Open reactions menu",
|
||||
"participants": "Participants",
|
||||
"party": "Celebration",
|
||||
"pip": "Enter Picture-in-Picture mode",
|
||||
"privateMessage": "Send private message",
|
||||
"profile": "Edit your profile",
|
||||
"raiseHand": "Raise / Lower your hand",
|
||||
"raiseYourHand": "Raise your hand",
|
||||
"reactionBoo": "Send boo reaction",
|
||||
"reactionClap": "Send clap reaction",
|
||||
"reactionLaugh": "Send laugh reaction",
|
||||
"reactionLike": "Send thumbs up reaction",
|
||||
"reactionParty": "Send party popper reaction",
|
||||
"reactionSurprised": "Send surprised reaction",
|
||||
"security": "Security options",
|
||||
"Settings": "Settings",
|
||||
"shareaudio": "Share audio",
|
||||
@@ -915,6 +953,7 @@
|
||||
"stopScreenSharing": "Stop screen sharing",
|
||||
"stopSubtitles": "Stop subtitles",
|
||||
"stopSharedVideo": "Stop video",
|
||||
"surprised": "Surprised",
|
||||
"talkWhileMutedPopup": "Trying to speak? You are muted.",
|
||||
"tileViewToggle": "Toggle tile view",
|
||||
"toggleCamera": "Toggle camera",
|
||||
@@ -1077,6 +1116,7 @@
|
||||
"passwordField": "Enter meeting password",
|
||||
"passwordJoinButton": "Join",
|
||||
"reject": "Reject",
|
||||
"rejectAll": "Reject all",
|
||||
"toggleLabel": "Enable lobby"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,9 @@ var loggingConfig = {
|
||||
// {@link #defaultLogLevel}:
|
||||
'modules/RTC/TraceablePeerConnection.js': 'info',
|
||||
'modules/statistics/CallStats.js': 'info',
|
||||
'modules/sdp/SDPUtil.js': 'info',
|
||||
'modules/xmpp/JingleSessionPC.js': 'info',
|
||||
'modules/xmpp/strophe.jingle.js': 'info',
|
||||
'modules/xmpp/strophe.util.js': 'log'
|
||||
};
|
||||
|
||||
|
||||
@@ -574,6 +574,12 @@ function initCommands() {
|
||||
});
|
||||
break;
|
||||
}
|
||||
case 'get-custom-avatar-backgrounds' : {
|
||||
callback({
|
||||
avatarBackgrounds: APP.store.getState()['features/dynamic-branding'].avatarBackgrounds
|
||||
});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -15,3 +15,8 @@ export const API_ID = parseURLParams(window.location).jitsi_meet_external_api_id
|
||||
* The payload name for the datachannel/endpoint text message event
|
||||
*/
|
||||
export const ENDPOINT_TEXT_MESSAGE_NAME = 'endpoint-text-message';
|
||||
|
||||
/**
|
||||
* The payload name for the datachannel/endpoint reaction event
|
||||
*/
|
||||
export const ENDPOINT_REACTION_NAME = 'endpoint-reaction';
|
||||
|
||||
13
modules/API/external/external_api.js
vendored
13
modules/API/external/external_api.js
vendored
@@ -170,7 +170,7 @@ function parseArguments(args) {
|
||||
|
||||
switch (typeof firstArg) {
|
||||
case 'string': // old arguments format
|
||||
case undefined: {
|
||||
case 'undefined': {
|
||||
// Not sure which format but we are trying to parse the old
|
||||
// format because if the new format is used everything will be undefined
|
||||
// anyway.
|
||||
@@ -769,6 +769,17 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
return getCurrentDevices(this._transport);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns any custom avatars backgrounds.
|
||||
*
|
||||
* @returns {Promise} - Resolves with the list of custom avatar backgrounds.
|
||||
*/
|
||||
getCustomAvatarBackgrounds() {
|
||||
return this._transport.sendRequest({
|
||||
name: 'get-custom-avatar-backgrounds'
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current livestream url.
|
||||
*
|
||||
|
||||
@@ -142,20 +142,23 @@ const KeyboardShortcut = {
|
||||
* @param exec the function to be executed when the shortcut is pressed
|
||||
* @param helpDescription the description of the shortcut that would appear
|
||||
* in the help menu
|
||||
* @param altKey whether or not the alt key must be pressed.
|
||||
*/
|
||||
registerShortcut(// eslint-disable-line max-params
|
||||
shortcutChar,
|
||||
shortcutAttr,
|
||||
exec,
|
||||
helpDescription) {
|
||||
_shortcuts.set(shortcutChar, {
|
||||
helpDescription,
|
||||
altKey = false) {
|
||||
_shortcuts.set(altKey ? `:${shortcutChar}` : shortcutChar, {
|
||||
character: shortcutChar,
|
||||
function: exec,
|
||||
shortcutAttr
|
||||
shortcutAttr,
|
||||
altKey
|
||||
});
|
||||
|
||||
if (helpDescription) {
|
||||
this._addShortcutToHelp(shortcutChar, helpDescription);
|
||||
this._addShortcutToHelp(altKey ? `:${shortcutChar}` : shortcutChar, helpDescription);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -164,9 +167,10 @@ const KeyboardShortcut = {
|
||||
*
|
||||
* @param shortcutChar unregisters the given shortcut, which means it will
|
||||
* no longer be usable
|
||||
* @param altKey whether or not shortcut is combo with alt key
|
||||
*/
|
||||
unregisterShortcut(shortcutChar) {
|
||||
_shortcuts.delete(shortcutChar);
|
||||
unregisterShortcut(shortcutChar, altKey = false) {
|
||||
_shortcuts.delete(altKey ? `:${shortcutChar}` : shortcutChar);
|
||||
_shortcutsHelp.delete(shortcutChar);
|
||||
},
|
||||
|
||||
@@ -175,6 +179,15 @@ const KeyboardShortcut = {
|
||||
* @returns {string} e.key or something close if not supported
|
||||
*/
|
||||
_getKeyboardKey(e) {
|
||||
// If alt is pressed a different char can be returned so this takes
|
||||
// the char from the code. It also prefixes with a colon to differentiate
|
||||
// alt combo from simple keypress.
|
||||
if (e.altKey) {
|
||||
const key = e.code.replace('Key', '');
|
||||
|
||||
return `:${key}`;
|
||||
}
|
||||
|
||||
// If e.key is a string, then it is assumed it already plainly states
|
||||
// the key pressed. This may not be true in all cases, such as with Edge
|
||||
// and "?", when the browser cannot properly map a key press event to a
|
||||
|
||||
66
package-lock.json
generated
66
package-lock.json
generated
@@ -2913,9 +2913,9 @@
|
||||
"integrity": "sha512-cPqjjzuFWNK3BSKLm0abspP0sp/IGOli4p5I5fKFAzdS8fvjdOwDCfZqAaIiXd9lPkOWi3SUUfZof3hEb7J/uw=="
|
||||
},
|
||||
"@react-native-async-storage/async-storage": {
|
||||
"version": "1.13.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.13.2.tgz",
|
||||
"integrity": "sha512-isTDvUApRJPVWFxV15yrQSOGqarX7cIedq/y4N5yWSnotf68D9qvDEv1I7rCXhkBDi0u4OJt6GA9dksUT0D3wg==",
|
||||
"version": "1.15.5",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.15.5.tgz",
|
||||
"integrity": "sha512-4AYehLH39B9a8UXCMf3ieOK+G61wGMP72ikx6/XSMA0DUnvx0PgaeaT2Wyt06kTrDTy8edewKnbrbeqwaM50TQ==",
|
||||
"requires": {
|
||||
"deep-assign": "^3.0.0"
|
||||
}
|
||||
@@ -3102,6 +3102,11 @@
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/netinfo/-/netinfo-4.1.5.tgz",
|
||||
"integrity": "sha512-lagdZr9UiVAccNXYfTEj+aUcPCx9ykbMe9puffeIyF3JsRuMmlu3BjHYx1klUHX7wNRmFNC8qVP0puxUt1sZ0A=="
|
||||
},
|
||||
"@react-native-community/slider": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/slider/-/slider-3.0.3.tgz",
|
||||
"integrity": "sha512-8IeHfDwJ9/CTUwFs6x90VlobV3BfuPgNLjTgC6dRZovfCWigaZwVNIFFJnHBakK3pW2xErAPwhdvNR4JeNoYbw=="
|
||||
},
|
||||
"@svgr/babel-plugin-add-jsx-attribute": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@svgr/babel-plugin-add-jsx-attribute/-/babel-plugin-add-jsx-attribute-4.2.0.tgz",
|
||||
@@ -7534,6 +7539,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"eme-encryption-scheme-polyfill": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/eme-encryption-scheme-polyfill/-/eme-encryption-scheme-polyfill-2.0.3.tgz",
|
||||
"integrity": "sha512-44CNFMsqzHdKHrzWxlS7xZ8KUHn5XutBqpmCuWzNIynmAyFInHrrD3ozv/RvK9ZhgV6QY6Easx8EWAmxteNodg=="
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-6.5.1.tgz",
|
||||
@@ -8239,7 +8249,8 @@
|
||||
"events": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.1.0.tgz",
|
||||
"integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg=="
|
||||
"integrity": "sha512-Rv+u8MLHNOdMjTAFeT3nCjHn2aGlx435FP/sDHNaRhDEMwyI/aB22Kj2qIN8R0cw3z28psEQLYwxVKLsKrMgWg==",
|
||||
"dev": true
|
||||
},
|
||||
"eventsource": {
|
||||
"version": "1.0.7",
|
||||
@@ -11011,6 +11022,11 @@
|
||||
"resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-2.2.0.tgz",
|
||||
"integrity": "sha1-fYa9VmefWM5qhHBKZX3TkruoGnk="
|
||||
},
|
||||
"keymirror": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/keymirror/-/keymirror-0.1.1.tgz",
|
||||
"integrity": "sha1-kYiJ6hP40KQufFVyUO7nE63JXDU="
|
||||
},
|
||||
"killable": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
|
||||
@@ -11071,8 +11087,8 @@
|
||||
}
|
||||
},
|
||||
"lib-jitsi-meet": {
|
||||
"version": "github:jitsi/lib-jitsi-meet#b43a9fa0eea73ba48d805fba97d0cd3fb9d8d07a",
|
||||
"from": "github:jitsi/lib-jitsi-meet#b43a9fa0eea73ba48d805fba97d0cd3fb9d8d07a",
|
||||
"version": "github:jitsi/lib-jitsi-meet#6a3df11ffa7a2204b579326e23cdaa85a79521d1",
|
||||
"from": "github:jitsi/lib-jitsi-meet#6a3df11ffa7a2204b579326e23cdaa85a79521d1",
|
||||
"requires": {
|
||||
"@jitsi/js-utils": "1.0.2",
|
||||
"@jitsi/sdp-interop": "github:jitsi/sdp-interop#5fc4af6dcf8a6e6af9fedbcd654412fd47b1b4ae",
|
||||
@@ -15138,14 +15154,25 @@
|
||||
"whatwg-url-without-unicode": "8.0.0-3"
|
||||
}
|
||||
},
|
||||
"react-native-video": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-video/-/react-native-video-5.1.1.tgz",
|
||||
"integrity": "sha512-zee8gRUrjPWRoZSEBiMebClqu1iAuCQNLjzqpmXFrRWEoJj7azM3BPqLQWJgsnfLiYUYGySeApC/G60THM5+tw==",
|
||||
"requires": {
|
||||
"keymirror": "^0.1.1",
|
||||
"prop-types": "^15.7.2",
|
||||
"shaka-player": "^2.5.9"
|
||||
}
|
||||
},
|
||||
"react-native-watch-connectivity": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-watch-connectivity/-/react-native-watch-connectivity-0.4.3.tgz",
|
||||
"integrity": "sha512-iqdJ1KpZbR4XGahgVmaeibB7kDhyMT7wrylINgJaYBY38IAiI0LF32VX1umO4pko6n21YF5I/kSeNQ+OXGqqow=="
|
||||
},
|
||||
"react-native-webrtc": {
|
||||
"version": "github:react-native-webrtc/react-native-webrtc#510d20dd62c1768885a98f36fde83f9e48a723fa",
|
||||
"from": "github:react-native-webrtc/react-native-webrtc#510d20dd62c1768885a98f36fde83f9e48a723fa",
|
||||
"version": "1.92.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-webrtc/-/react-native-webrtc-1.92.0.tgz",
|
||||
"integrity": "sha512-nztKQ/SmO1DgA3QWCDHHK8ZVDf+5rLbmH42Ukoqnld7ut8/ehmFZXc17aSV/BN0H60jPigGqAMYopt/LZak7Sg==",
|
||||
"requires": {
|
||||
"base64-js": "^1.1.2",
|
||||
"cross-os": "^1.3.0",
|
||||
@@ -15186,11 +15213,18 @@
|
||||
}
|
||||
},
|
||||
"react-native-youtube-iframe": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-youtube-iframe/-/react-native-youtube-iframe-1.2.3.tgz",
|
||||
"integrity": "sha512-3O8OFJyohGNlYX4D97aWfLLlhEHhlLHDCLgXM+SsQBwP9r1oLnKgXWoy1gce+Vr8qgrqeQgmx1ki+10AAd4KWQ==",
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-youtube-iframe/-/react-native-youtube-iframe-2.1.1.tgz",
|
||||
"integrity": "sha512-vnLzA5zcnMwa1gMqGfvkjaE82NW1Nd2Up4Q1OUz6IKm69xSG/9/m4APZ5fCN8UMhy6lH95iagd497J7jwEwz3w==",
|
||||
"requires": {
|
||||
"events": "^3.0.0"
|
||||
"events": "^3.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"events": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-node-resolver": {
|
||||
@@ -16064,6 +16098,14 @@
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"shaka-player": {
|
||||
"version": "2.5.22",
|
||||
"resolved": "https://registry.npmjs.org/shaka-player/-/shaka-player-2.5.22.tgz",
|
||||
"integrity": "sha512-PAoeNLUQ/hT/9dY7QvNFgIiDtXSqbYVFuXXtLHh7ytVVqTvI/p4HLwfYShiR+sE/sbsDOr9D5l9D/ztLPhxgtw==",
|
||||
"requires": {
|
||||
"eme-encryption-scheme-polyfill": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"shallow-clone": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-0.1.2.tgz",
|
||||
|
||||
10
package.json
10
package.json
@@ -36,9 +36,10 @@
|
||||
"@jitsi/js-utils": "1.0.6",
|
||||
"@material-ui/core": "4.11.3",
|
||||
"@microsoft/microsoft-graph-client": "1.1.0",
|
||||
"@react-native-async-storage/async-storage": "1.13.2",
|
||||
"@react-native-async-storage/async-storage": "1.15.5",
|
||||
"@react-native-community/google-signin": "3.0.1",
|
||||
"@react-native-community/netinfo": "4.1.5",
|
||||
"@react-native-community/slider": "3.0.3",
|
||||
"@svgr/webpack": "4.3.2",
|
||||
"amplitude-js": "8.2.1",
|
||||
"base64-js": "1.3.1",
|
||||
@@ -55,7 +56,7 @@
|
||||
"jquery-i18next": "1.2.1",
|
||||
"js-md5": "0.6.1",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#b43a9fa0eea73ba48d805fba97d0cd3fb9d8d07a",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#6a3df11ffa7a2204b579326e23cdaa85a79521d1",
|
||||
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.1",
|
||||
@@ -86,10 +87,11 @@
|
||||
"react-native-svg": "12.1.0",
|
||||
"react-native-svg-transformer": "0.14.3",
|
||||
"react-native-url-polyfill": "1.2.0",
|
||||
"react-native-video": "5.1.1",
|
||||
"react-native-watch-connectivity": "0.4.3",
|
||||
"react-native-webrtc": "github:react-native-webrtc/react-native-webrtc#510d20dd62c1768885a98f36fde83f9e48a723fa",
|
||||
"react-native-webrtc": "1.92.0",
|
||||
"react-native-webview": "11.0.2",
|
||||
"react-native-youtube-iframe": "1.2.3",
|
||||
"react-native-youtube-iframe": "2.1.1",
|
||||
"react-redux": "7.1.0",
|
||||
"react-textarea-autosize": "8.3.0",
|
||||
"react-transition-group": "2.4.0",
|
||||
|
||||
@@ -21,6 +21,7 @@ const TOOLBAR_TIMEOUT = 4000;
|
||||
*/
|
||||
type State = {
|
||||
avatarURL: string,
|
||||
customAvatarBackgrounds: Array<string>,
|
||||
displayName: string,
|
||||
formattedDisplayName: string,
|
||||
isVideoDisplayed: boolean,
|
||||
@@ -48,6 +49,7 @@ export default class AlwaysOnTop extends Component<*, State> {
|
||||
|
||||
this.state = {
|
||||
avatarURL: '',
|
||||
customAvatarBackgrounds: [],
|
||||
displayName: '',
|
||||
formattedDisplayName: '',
|
||||
isVideoDisplayed: true,
|
||||
@@ -178,7 +180,14 @@ export default class AlwaysOnTop extends Component<*, State> {
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderVideoNotAvailableScreen() {
|
||||
const { avatarURL, displayName, formattedDisplayName, isVideoDisplayed, userID } = this.state;
|
||||
const {
|
||||
avatarURL,
|
||||
customAvatarBackgrounds,
|
||||
displayName,
|
||||
formattedDisplayName,
|
||||
isVideoDisplayed,
|
||||
userID
|
||||
} = this.state;
|
||||
|
||||
if (isVideoDisplayed) {
|
||||
return null;
|
||||
@@ -188,7 +197,7 @@ export default class AlwaysOnTop extends Component<*, State> {
|
||||
<div id = 'videoNotAvailableScreen'>
|
||||
<div id = 'avatarContainer'>
|
||||
<StatelessAvatar
|
||||
color = { getAvatarColor(userID) }
|
||||
color = { getAvatarColor(userID, customAvatarBackgrounds) }
|
||||
id = 'avatar'
|
||||
initials = { getInitials(displayName) }
|
||||
url = { avatarURL } />)
|
||||
@@ -218,6 +227,12 @@ export default class AlwaysOnTop extends Component<*, State> {
|
||||
window.addEventListener('mousemove', this._mouseMove);
|
||||
|
||||
this._hideToolbarAfterTimeout();
|
||||
api.getCustomAvatarBackgrounds()
|
||||
.then(res =>
|
||||
this.setState({
|
||||
customAvatarBackgrounds: res.avatarBackgrounds || []
|
||||
}))
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -795,6 +795,23 @@ export function createToolbarEvent(buttonName, attributes = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event associated with a reaction button being clicked/pressed.
|
||||
*
|
||||
* @param {string} buttonName - The identifier of the reaction button which was
|
||||
* clicked/pressed.
|
||||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export function createReactionMenuEvent(buttonName) {
|
||||
return {
|
||||
action: 'clicked',
|
||||
actionSubject: buttonName,
|
||||
source: 'reaction.button',
|
||||
type: TYPE_UI
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event which indicates that a local track was muted.
|
||||
*
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
parseURIString,
|
||||
toURLString
|
||||
} from '../base/util';
|
||||
import { isVpaasMeeting } from '../billing-counter/functions';
|
||||
import { isVpaasMeeting } from '../jaas/functions';
|
||||
import { clearNotifications, showNotification } from '../notifications';
|
||||
import { setFatalError } from '../overlay';
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import { getFeatureFlag } from '../../base/flags/functions';
|
||||
import { Platform } from '../../base/react';
|
||||
import { DimensionsDetector, clientResized } from '../../base/responsive-ui';
|
||||
import { updateSettings } from '../../base/settings';
|
||||
import JitsiThemePaperProvider from '../../base/ui/components/JitsiThemeProvider.native';
|
||||
import logger from '../logger';
|
||||
|
||||
import { AbstractApp } from './AbstractApp';
|
||||
@@ -128,12 +127,10 @@ export class App extends AbstractApp {
|
||||
*/
|
||||
_createMainElement(component, props) {
|
||||
return (
|
||||
<JitsiThemePaperProvider>
|
||||
<DimensionsDetector
|
||||
onDimensionsChanged = { this._onDimensionsChanged }>
|
||||
{ super._createMainElement(component, props) }
|
||||
</DimensionsDetector>
|
||||
</JitsiThemePaperProvider>
|
||||
<DimensionsDetector
|
||||
onDimensionsChanged = { this._onDimensionsChanged }>
|
||||
{ super._createMainElement(component, props) }
|
||||
</DimensionsDetector>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ import '../base/sounds/middleware';
|
||||
import '../base/testing/middleware';
|
||||
import '../base/tracks/middleware';
|
||||
import '../base/user-interaction/middleware';
|
||||
import '../billing-counter/middleware';
|
||||
import '../calendar-sync/middleware';
|
||||
import '../chat/middleware';
|
||||
import '../conference/middleware';
|
||||
@@ -35,6 +34,7 @@ import '../large-video/middleware';
|
||||
import '../lobby/middleware';
|
||||
import '../notifications/middleware';
|
||||
import '../overlay/middleware';
|
||||
import '../reactions/middleware';
|
||||
import '../recent-list/middleware';
|
||||
import '../recording/middleware';
|
||||
import '../rejoin/middleware';
|
||||
|
||||
@@ -25,7 +25,6 @@ import '../base/sounds/reducer';
|
||||
import '../base/testing/reducer';
|
||||
import '../base/tracks/reducer';
|
||||
import '../base/user-interaction/reducer';
|
||||
import '../billing-counter/reducer';
|
||||
import '../calendar-sync/reducer';
|
||||
import '../chat/reducer';
|
||||
import '../deep-linking/reducer';
|
||||
@@ -41,6 +40,8 @@ import '../large-video/reducer';
|
||||
import '../lobby/reducer';
|
||||
import '../notifications/reducer';
|
||||
import '../overlay/reducer';
|
||||
import '../participants-pane/reducer';
|
||||
import '../reactions/reducer';
|
||||
import '../recent-list/reducer';
|
||||
import '../recording/reducer';
|
||||
import '../settings/reducer';
|
||||
|
||||
@@ -10,6 +10,11 @@ import { StatelessAvatar } from '.';
|
||||
|
||||
export type Props = {
|
||||
|
||||
/**
|
||||
* Custom avatar backgrounds from branding.
|
||||
*/
|
||||
_customAvatarBackgrounds: Array<string>,
|
||||
|
||||
/**
|
||||
* The string we base the initials on (this is generated from a list of precedences).
|
||||
*/
|
||||
@@ -133,6 +138,7 @@ class Avatar<P: Props> extends PureComponent<P, State> {
|
||||
*/
|
||||
render() {
|
||||
const {
|
||||
_customAvatarBackgrounds,
|
||||
_initialsBase,
|
||||
_loadableAvatarUrl,
|
||||
className,
|
||||
@@ -172,7 +178,7 @@ class Avatar<P: Props> extends PureComponent<P, State> {
|
||||
|
||||
if (initials) {
|
||||
if (dynamicColor) {
|
||||
avatarProps.color = getAvatarColor(colorBase || _initialsBase);
|
||||
avatarProps.color = getAvatarColor(colorBase || _initialsBase, _customAvatarBackgrounds);
|
||||
}
|
||||
|
||||
avatarProps.initials = initials;
|
||||
@@ -211,6 +217,7 @@ export function _mapStateToProps(state: Object, ownProps: Props) {
|
||||
const _initialsBase = _participant?.name ?? displayName;
|
||||
|
||||
return {
|
||||
_customAvatarBackgrounds: state['features/dynamic-branding'].avatarBackgrounds,
|
||||
_initialsBase,
|
||||
_loadableAvatarUrl: _participant?.loadableAvatarUrl,
|
||||
colorBase: !colorBase && _participant ? _participant.id : colorBase
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import { translate } from '../../../../base/i18n';
|
||||
import { Icon } from '../../../icons';
|
||||
import AbstractStatelessAvatar, { type Props as AbstractProps } from '../AbstractStatelessAvatar';
|
||||
|
||||
@@ -31,19 +30,14 @@ type Props = AbstractProps & {
|
||||
/**
|
||||
* TestId of the element, if any.
|
||||
*/
|
||||
testId?: string,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
t: Function
|
||||
testId?: string
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements a stateless avatar component that renders an avatar purely from what gets passed through
|
||||
* props.
|
||||
*/
|
||||
class StatelessAvatar extends AbstractStatelessAvatar<Props> {
|
||||
export default class StatelessAvatar extends AbstractStatelessAvatar<Props> {
|
||||
/**
|
||||
* Implements {@code Component#render}.
|
||||
*
|
||||
@@ -70,7 +64,7 @@ class StatelessAvatar extends AbstractStatelessAvatar<Props> {
|
||||
return (
|
||||
<div className = { this._getBadgeClassName() }>
|
||||
<img
|
||||
alt = { this.props.t('profile.avatar') }
|
||||
alt = 'avatar'
|
||||
className = { this._getAvatarClassName() }
|
||||
data-testid = { this.props.testId }
|
||||
id = { this.props.id }
|
||||
@@ -111,7 +105,7 @@ class StatelessAvatar extends AbstractStatelessAvatar<Props> {
|
||||
return (
|
||||
<div className = { this._getBadgeClassName() }>
|
||||
<img
|
||||
alt = { this.props.t('profile.avatar') }
|
||||
alt = 'avatar'
|
||||
className = { this._getAvatarClassName('defaultAvatar') }
|
||||
data-testid = { this.props.testId }
|
||||
id = { this.props.id }
|
||||
@@ -131,7 +125,7 @@ class StatelessAvatar extends AbstractStatelessAvatar<Props> {
|
||||
const { size } = this.props;
|
||||
|
||||
return {
|
||||
backgroundColor: color || undefined,
|
||||
background: color || undefined,
|
||||
fontSize: size ? size * 0.5 : '180%',
|
||||
height: size || '100%',
|
||||
width: size || '100%'
|
||||
@@ -165,5 +159,3 @@ class StatelessAvatar extends AbstractStatelessAvatar<Props> {
|
||||
|
||||
_isIcon: (?string | ?Object) => boolean
|
||||
}
|
||||
|
||||
export default translate(StatelessAvatar);
|
||||
|
||||
@@ -16,9 +16,13 @@ const AVATAR_OPACITY = 0.4;
|
||||
* Generates the background color of an initials based avatar.
|
||||
*
|
||||
* @param {string?} initials - The initials of the avatar.
|
||||
* @param {Array<strig>} customAvatarBackgrounds - Custom avatar background values.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getAvatarColor(initials: ?string) {
|
||||
export function getAvatarColor(initials: ?string, customAvatarBackgrounds: Array<string>) {
|
||||
const hasCustomAvatarBackgronds = customAvatarBackgrounds && customAvatarBackgrounds.length;
|
||||
const colorsBase = hasCustomAvatarBackgronds ? customAvatarBackgrounds : AVATAR_COLORS;
|
||||
|
||||
let colorIndex = 0;
|
||||
|
||||
if (initials) {
|
||||
@@ -28,10 +32,10 @@ export function getAvatarColor(initials: ?string) {
|
||||
nameHash += s.codePointAt(0);
|
||||
}
|
||||
|
||||
colorIndex = nameHash % AVATAR_COLORS.length;
|
||||
colorIndex = nameHash % colorsBase.length;
|
||||
}
|
||||
|
||||
return `rgba(${AVATAR_COLORS[colorIndex]}, ${AVATAR_OPACITY})`;
|
||||
return hasCustomAvatarBackgronds ? colorsBase[colorIndex] : `rgba(${colorsBase[colorIndex]}, ${AVATAR_OPACITY})`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
createStartMutedConfigurationEvent,
|
||||
sendAnalytics
|
||||
} from '../../analytics';
|
||||
import { getName } from '../../app/functions';
|
||||
import { endpointMessageReceived } from '../../subtitles';
|
||||
import { getReplaceParticipant } from '../config/functions';
|
||||
import { JITSI_CONNECTION_CONFERENCE_KEY } from '../connection';
|
||||
@@ -14,7 +13,6 @@ import { JitsiConferenceEvents } from '../lib-jitsi-meet';
|
||||
import { MEDIA_TYPE, setAudioMuted, setVideoMuted } from '../media';
|
||||
import {
|
||||
dominantSpeakerChanged,
|
||||
getLocalParticipant,
|
||||
getNormalizedDisplayName,
|
||||
participantConnectionStatusChanged,
|
||||
participantKicked,
|
||||
@@ -24,11 +22,7 @@ import {
|
||||
participantUpdated
|
||||
} from '../participants';
|
||||
import { getLocalTracks, replaceLocalTrack, trackAdded, trackRemoved } from '../tracks';
|
||||
import {
|
||||
getBackendSafePath,
|
||||
getBackendSafeRoomName,
|
||||
getJitsiMeetGlobalNS
|
||||
} from '../util';
|
||||
import { getBackendSafeRoomName } from '../util';
|
||||
|
||||
import {
|
||||
AUTH_STATUS_CHANGED,
|
||||
@@ -61,6 +55,7 @@ import {
|
||||
_addLocalTracksToConference,
|
||||
commonUserJoinedHandling,
|
||||
commonUserLeftHandling,
|
||||
getConferenceOptions,
|
||||
getCurrentConference,
|
||||
sendLocalParticipant
|
||||
} from './functions';
|
||||
@@ -434,22 +429,7 @@ export function createConference() {
|
||||
throw new Error('Cannot join a conference without a room name!');
|
||||
}
|
||||
|
||||
const config = state['features/base/config'];
|
||||
const { tenant } = state['features/base/jwt'];
|
||||
const { email, name: nick } = getLocalParticipant(state);
|
||||
|
||||
const conference
|
||||
= connection.initJitsiConference(
|
||||
|
||||
getBackendSafeRoomName(room), {
|
||||
...config,
|
||||
applicationName: getName(),
|
||||
getWiFiStatsMethod: getJitsiMeetGlobalNS().getWiFiStats,
|
||||
confID: `${locationURL.host}${getBackendSafePath(locationURL.pathname)}`,
|
||||
siteID: tenant,
|
||||
statisticsDisplayName: config.enableDisplayNameInStats ? nick : undefined,
|
||||
statisticsId: config.enableEmailInStats ? email : undefined
|
||||
});
|
||||
const conference = connection.initJitsiConference(getBackendSafeRoomName(room), getConferenceOptions(state));
|
||||
|
||||
connection[JITSI_CONNECTION_CONFERENCE_KEY] = conference;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
import { getName } from '../../app/functions';
|
||||
import { JitsiTrackErrors } from '../lib-jitsi-meet';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
@@ -11,7 +12,7 @@ import {
|
||||
participantLeft
|
||||
} from '../participants';
|
||||
import { toState } from '../redux';
|
||||
import { safeDecodeURIComponent } from '../util';
|
||||
import { getBackendSafePath, getJitsiMeetGlobalNS, safeDecodeURIComponent } from '../util';
|
||||
|
||||
import {
|
||||
AVATAR_URL_COMMAND,
|
||||
@@ -198,6 +199,53 @@ export function getConferenceNameForTitle(stateful: Function | Object) {
|
||||
return safeStartCase(safeDecodeURIComponent(getConferenceState(toState(stateful)).room));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object aggregating the conference options.
|
||||
*
|
||||
* @param {Object|Function} stateful - The redux store state.
|
||||
* @returns {Object} - Options object.
|
||||
*/
|
||||
export function getConferenceOptions(stateful: Function | Object) {
|
||||
const state = toState(stateful);
|
||||
|
||||
const config = state['features/base/config'];
|
||||
const { locationURL } = state['features/base/connection'];
|
||||
const { tenant } = state['features/base/jwt'];
|
||||
const { email, name: nick } = getLocalParticipant(state);
|
||||
const options = { ...config };
|
||||
|
||||
if (tenant) {
|
||||
options.siteID = tenant;
|
||||
}
|
||||
|
||||
if (options.enableDisplayNameInStats && nick) {
|
||||
options.statisticsDisplayName = nick;
|
||||
}
|
||||
|
||||
if (options.enableEmailInStats && email) {
|
||||
options.statisticsId = email;
|
||||
}
|
||||
|
||||
if (locationURL) {
|
||||
options.confID = `${locationURL.host}${getBackendSafePath(locationURL.pathname)}`;
|
||||
}
|
||||
|
||||
options.applicationName = getName();
|
||||
|
||||
// Disable analytics, if requessted.
|
||||
if (options.disableThirdPartyRequests) {
|
||||
delete config.analytics.scriptURLs;
|
||||
delete config.analytics.amplitudeAPPKey;
|
||||
delete config.analytics.googleAnalyticsTrackingId;
|
||||
delete options.callStatsID;
|
||||
delete options.callStatsSecret;
|
||||
} else {
|
||||
options.getWiFiStatsMethod = getWiFiStatsMethod;
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the UTC timestamp when the first participant joined the conference.
|
||||
*
|
||||
@@ -244,6 +292,21 @@ export function getRoomName(state: Object): string {
|
||||
return getConferenceState(state).room;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the result of getWiFiStats from the global NS or does nothing
|
||||
* (returns empty result).
|
||||
* Fixes a concurrency problem where we need to pass a function when creating
|
||||
* a JitsiConference, but that method is added to the context later.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* @private
|
||||
*/
|
||||
function getWiFiStatsMethod() {
|
||||
const gloabalNS = getJitsiMeetGlobalNS();
|
||||
|
||||
return gloabalNS.getWiFiStats ? gloabalNS.getWiFiStats() : Promise.resolve('{}');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an error thrown by the backend (i.e. {@code lib-jitsi-meet}) while
|
||||
* manipulating a conference participant (e.g. Pin or select participant).
|
||||
|
||||
@@ -87,6 +87,7 @@ export default [
|
||||
'disableH264',
|
||||
'disableHPF',
|
||||
'disableInviteFunctions',
|
||||
'disableIncomingMessageSound',
|
||||
'disableJoinLeaveSounds',
|
||||
'disableLocalVideoFlip',
|
||||
'disableNS',
|
||||
@@ -130,6 +131,7 @@ export default [
|
||||
'gatherStats',
|
||||
'googleApiApplicationClientID',
|
||||
'hideConferenceSubject',
|
||||
'hideRecordingLabel',
|
||||
'hideParticipantsStats',
|
||||
'hideConferenceTimer',
|
||||
'hiddenDomain',
|
||||
|
||||
@@ -14,10 +14,35 @@ export const _CONFIG_STORE_PREFIX = 'config.js';
|
||||
* @type Array<string>
|
||||
*/
|
||||
export const TOOLBAR_BUTTONS = [
|
||||
'microphone', 'camera', 'closedcaptions', 'desktop', 'embedmeeting', 'fullscreen',
|
||||
'fodeviceselection', 'hangup', 'profile', 'chat', 'recording',
|
||||
'livestreaming', 'etherpad', 'sharedvideo', 'shareaudio', 'settings', 'raisehand',
|
||||
'videoquality', 'filmstrip', 'participants-pane', 'feedback', 'stats', 'shortcuts',
|
||||
'tileview', 'select-background', 'download', 'help', 'mute-everyone', 'mute-video-everyone',
|
||||
'security'
|
||||
'camera',
|
||||
'chat',
|
||||
'closedcaptions',
|
||||
'desktop',
|
||||
'download',
|
||||
'embedmeeting',
|
||||
'etherpad',
|
||||
'feedback',
|
||||
'filmstrip',
|
||||
'fullscreen',
|
||||
'hangup',
|
||||
'help',
|
||||
'invite',
|
||||
'livestreaming',
|
||||
'microphone',
|
||||
'mute-everyone',
|
||||
'mute-video-everyone',
|
||||
'participants-pane',
|
||||
'profile',
|
||||
'raisehand',
|
||||
'recording',
|
||||
'security',
|
||||
'select-background',
|
||||
'settings',
|
||||
'shareaudio',
|
||||
'sharedvideo',
|
||||
'shortcuts',
|
||||
'stats',
|
||||
'tileview',
|
||||
'toggle-camera',
|
||||
'videoquality'
|
||||
];
|
||||
|
||||
@@ -60,8 +60,6 @@ export function getRecordingSharingUrl(state: Object) {
|
||||
return state['features/base/config'].recordingSharingUrl;
|
||||
}
|
||||
|
||||
/* eslint-disable max-params, no-shadow */
|
||||
|
||||
/**
|
||||
* Overrides JSON properties in {@code config} and
|
||||
* {@code interfaceConfig} Objects with the values from {@code newConfig}.
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { type ReactionEmojiProps } from '../../../reactions/constants';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link DialogContainer}.
|
||||
*/
|
||||
@@ -25,7 +27,12 @@ type Props = {
|
||||
/**
|
||||
* True if the UI is in a compact state where we don't show dialogs.
|
||||
*/
|
||||
_reducedUI: boolean
|
||||
_reducedUI: boolean,
|
||||
|
||||
/**
|
||||
* Array of reactions to be displayed.
|
||||
*/
|
||||
_reactionsQueue: Array<ReactionEmojiProps>
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,11 +25,21 @@ const GESTURE_SPEED_THRESHOLD = 0.2;
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The height of the screen.
|
||||
*/
|
||||
_height: number,
|
||||
|
||||
/**
|
||||
* The color-schemed stylesheet of the feature.
|
||||
*/
|
||||
_styles: StyleType,
|
||||
|
||||
/**
|
||||
* Whether to add padding to scroll view.
|
||||
*/
|
||||
addScrollViewPadding?: boolean,
|
||||
|
||||
/**
|
||||
* The children to be displayed within this component.
|
||||
*/
|
||||
@@ -49,7 +59,22 @@ type Props = {
|
||||
/**
|
||||
* Function to render a bottom sheet header element, if necessary.
|
||||
*/
|
||||
renderHeader: ?Function
|
||||
renderHeader: ?Function,
|
||||
|
||||
/**
|
||||
* Function to render a bottom sheet footer element, if necessary.
|
||||
*/
|
||||
renderFooter: ?Function,
|
||||
|
||||
/**
|
||||
* Whether to show sliding view or not.
|
||||
*/
|
||||
showSlidingView?: boolean,
|
||||
|
||||
/**
|
||||
* The component's external style
|
||||
*/
|
||||
style: Object
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -58,6 +83,16 @@ type Props = {
|
||||
class BottomSheet extends PureComponent<Props> {
|
||||
panResponder: Object;
|
||||
|
||||
/**
|
||||
* Default values for {@code BottomSheet} component's properties.
|
||||
*
|
||||
* @static
|
||||
*/
|
||||
static defaultProps = {
|
||||
addScrollViewPadding: true,
|
||||
showSlidingView: true
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiates a new component.
|
||||
*
|
||||
@@ -80,7 +115,15 @@ class BottomSheet extends PureComponent<Props> {
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
const { _styles, renderHeader } = this.props;
|
||||
const {
|
||||
_height,
|
||||
_styles,
|
||||
addScrollViewPadding,
|
||||
renderHeader,
|
||||
renderFooter,
|
||||
showSlidingView,
|
||||
style
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<SlidingView
|
||||
@@ -88,7 +131,7 @@ class BottomSheet extends PureComponent<Props> {
|
||||
accessibilityViewIsModal = { true }
|
||||
onHide = { this.props.onCancel }
|
||||
position = 'bottom'
|
||||
show = { true }>
|
||||
show = { showSlidingView }>
|
||||
<View
|
||||
pointerEvents = 'box-none'
|
||||
style = { styles.sheetContainer }>
|
||||
@@ -99,15 +142,22 @@ class BottomSheet extends PureComponent<Props> {
|
||||
<SafeAreaView
|
||||
style = { [
|
||||
styles.sheetItemContainer,
|
||||
_styles.sheet
|
||||
renderHeader
|
||||
? _styles.sheetHeader
|
||||
: _styles.sheet,
|
||||
style,
|
||||
{
|
||||
maxHeight: _height - 100
|
||||
}
|
||||
] }
|
||||
{ ...this.panResponder.panHandlers }>
|
||||
<ScrollView
|
||||
bounces = { false }
|
||||
showsVerticalScrollIndicator = { false }
|
||||
style = { styles.scrollView } >
|
||||
style = { addScrollViewPadding && styles.scrollView } >
|
||||
{ this.props.children }
|
||||
</ScrollView>
|
||||
{ renderFooter && renderFooter() }
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
</SlidingView>
|
||||
@@ -167,7 +217,8 @@ class BottomSheet extends PureComponent<Props> {
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
return {
|
||||
_styles: ColorSchemeRegistry.get(state, 'BottomSheet')
|
||||
_styles: ColorSchemeRegistry.get(state, 'BottomSheet'),
|
||||
_height: state['features/base/responsive-ui'].clientHeight
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { ReactionEmoji } from '../../../../reactions/components';
|
||||
import { getReactionsQueue } from '../../../../reactions/functions.any';
|
||||
import { connect } from '../../../redux';
|
||||
import AbstractDialogContainer, {
|
||||
abstractMapStateToProps
|
||||
@@ -11,6 +15,22 @@ import AbstractDialogContainer, {
|
||||
* @extends AbstractDialogContainer
|
||||
*/
|
||||
class DialogContainer extends AbstractDialogContainer {
|
||||
|
||||
/**
|
||||
* Returns the reactions to be displayed.
|
||||
*
|
||||
* @returns {Array<React$Element>}
|
||||
*/
|
||||
_renderReactions() {
|
||||
const { _reactionsQueue } = this.props;
|
||||
|
||||
return _reactionsQueue.map(({ reaction, uid }, index) => (<ReactionEmoji
|
||||
index = { index }
|
||||
key = { uid }
|
||||
reaction = { reaction }
|
||||
uid = { uid } />));
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
@@ -18,8 +38,18 @@ class DialogContainer extends AbstractDialogContainer {
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
render() {
|
||||
return this._renderDialogContent();
|
||||
return (<React.Fragment>
|
||||
{this._renderReactions()}
|
||||
{this._renderDialogContent()}
|
||||
</React.Fragment>);
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(abstractMapStateToProps)(DialogContainer);
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
...abstractMapStateToProps(state),
|
||||
_reactionsQueue: getReactionsQueue(state)
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(DialogContainer);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import BaseTheme from '../../../../base/ui/components/BaseTheme.native';
|
||||
import { ColorSchemeRegistry, schemeColor } from '../../../color-scheme';
|
||||
import { BoxModel, ColorPalette } from '../../../styles';
|
||||
import { PREFERRED_DIALOG_SIZE } from '../../constants';
|
||||
@@ -33,7 +34,7 @@ export const bottomSheetStyles = {
|
||||
},
|
||||
|
||||
scrollView: {
|
||||
paddingHorizontal: MD_ITEM_MARGIN_PADDING
|
||||
paddingHorizontal: 0
|
||||
},
|
||||
|
||||
/**
|
||||
@@ -117,7 +118,7 @@ const brandedDialogText = {
|
||||
};
|
||||
|
||||
const brandedDialogLabelStyle = {
|
||||
color: schemeColor('text'),
|
||||
color: ColorPalette.white,
|
||||
flexShrink: 1,
|
||||
fontSize: MD_FONT_SIZE,
|
||||
opacity: 0.90
|
||||
@@ -130,7 +131,7 @@ const brandedDialogItemContainerStyle = {
|
||||
};
|
||||
|
||||
const brandedDialogIconStyle = {
|
||||
color: schemeColor('icon'),
|
||||
color: ColorPalette.white,
|
||||
fontSize: 24
|
||||
};
|
||||
|
||||
@@ -171,27 +172,37 @@ ColorSchemeRegistry.register('BottomSheet', {
|
||||
*/
|
||||
labelStyle: {
|
||||
...brandedDialogLabelStyle,
|
||||
marginLeft: 32
|
||||
marginLeft: 16
|
||||
},
|
||||
|
||||
/**
|
||||
* Container style for a generic item rendered in the menu.
|
||||
*/
|
||||
style: {
|
||||
...brandedDialogItemContainerStyle
|
||||
...brandedDialogItemContainerStyle,
|
||||
paddingHorizontal: MD_ITEM_MARGIN_PADDING
|
||||
},
|
||||
|
||||
/**
|
||||
* Additional style that is not directly used as a style object.
|
||||
*/
|
||||
underlayColor: ColorPalette.overflowMenuItemUnderlay
|
||||
underlayColor: ColorPalette.toggled
|
||||
},
|
||||
|
||||
/**
|
||||
* Bottom sheet's base style.
|
||||
*/
|
||||
sheet: {
|
||||
backgroundColor: schemeColor('background')
|
||||
backgroundColor: BaseTheme.palette.ui02,
|
||||
borderTopLeftRadius: 16,
|
||||
borderTopRightRadius: 16
|
||||
},
|
||||
|
||||
/**
|
||||
* Bottom sheet's base style with header.
|
||||
*/
|
||||
sheetHeader: {
|
||||
backgroundColor: BaseTheme.palette.ui02
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -214,3 +214,9 @@ export const VIDEO_SHARE_BUTTON_ENABLED = 'video-share.enabled';
|
||||
* Default: disabled (false).
|
||||
*/
|
||||
export const WELCOME_PAGE_ENABLED = 'welcomepage.enabled';
|
||||
|
||||
/**
|
||||
* Flag indicating if the reactions feature should be enabled.
|
||||
* Default: disabled (false).
|
||||
*/
|
||||
export const REACTIONS_ENABLED = 'reactions.enabled';
|
||||
|
||||
@@ -50,6 +50,11 @@ type Props = {
|
||||
*/
|
||||
headerProps: Object,
|
||||
|
||||
/**
|
||||
* True if the header with navigation should be hidden, false otherwise.
|
||||
*/
|
||||
hideHeaderWithNavigation?: boolean,
|
||||
|
||||
/**
|
||||
* The ID of the modal that is being rendered. This is used to show/hide the modal.
|
||||
*/
|
||||
@@ -78,7 +83,8 @@ type Props = {
|
||||
*/
|
||||
class JitsiModal extends PureComponent<Props> {
|
||||
static defaultProps = {
|
||||
position: 'bottom'
|
||||
position: 'bottom',
|
||||
hideHeaderWithNavigation: false
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -98,7 +104,17 @@ class JitsiModal extends PureComponent<Props> {
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { _headerStyles, _show, _styles, children, footerComponent, headerProps, position, style } = this.props;
|
||||
const {
|
||||
_headerStyles,
|
||||
_show,
|
||||
_styles,
|
||||
children,
|
||||
footerComponent,
|
||||
headerProps,
|
||||
position,
|
||||
hideHeaderWithNavigation,
|
||||
style
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<SlidingView
|
||||
@@ -119,6 +135,7 @@ class JitsiModal extends PureComponent<Props> {
|
||||
] }>
|
||||
<HeaderWithNavigation
|
||||
{ ...headerProps }
|
||||
hideHeaderWithNavigation = { hideHeaderWithNavigation }
|
||||
onPressBack = { this._onRequestClose } />
|
||||
<SafeAreaView style = { styles.safeArea }>
|
||||
{ children }
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import { Text, View } from 'react-native';
|
||||
|
||||
import { YoutubeLargeVideo } from '../../../shared-video/components';
|
||||
import { SharedVideo } from '../../../shared-video/components/native';
|
||||
import { Avatar } from '../../avatar';
|
||||
import { translate } from '../../i18n';
|
||||
import { JitsiParticipantConnectionStatus } from '../../lib-jitsi-meet';
|
||||
@@ -208,11 +208,11 @@ class ParticipantView extends Component<Props> {
|
||||
? this.props.testHintId
|
||||
: `org.jitsi.meet.Participant#${this.props.participantId}`;
|
||||
|
||||
const renderYoutubeLargeVideo = _isFakeParticipant && !disableVideo;
|
||||
const renderSharedVideo = _isFakeParticipant && !disableVideo;
|
||||
|
||||
return (
|
||||
<Container
|
||||
onClick = { renderVideo || renderYoutubeLargeVideo ? undefined : onPress }
|
||||
onClick = { renderVideo || renderSharedVideo ? undefined : onPress }
|
||||
style = {{
|
||||
...styles.participantView,
|
||||
...this.props.style
|
||||
@@ -221,10 +221,10 @@ class ParticipantView extends Component<Props> {
|
||||
|
||||
<TestHint
|
||||
id = { testHintId }
|
||||
onPress = { renderYoutubeLargeVideo ? undefined : onPress }
|
||||
onPress = { renderSharedVideo ? undefined : onPress }
|
||||
value = '' />
|
||||
|
||||
{ renderYoutubeLargeVideo && <YoutubeLargeVideo youtubeId = { this.props.participantId } /> }
|
||||
{ renderSharedVideo && <SharedVideo /> }
|
||||
|
||||
{ !_isFakeParticipant && renderVideo
|
||||
&& <VideoTrack
|
||||
@@ -234,7 +234,7 @@ class ParticipantView extends Component<Props> {
|
||||
zOrder = { this.props.zOrder }
|
||||
zoomEnabled = { this.props.zoomEnabled } /> }
|
||||
|
||||
{ !renderYoutubeLargeVideo && !renderVideo
|
||||
{ !renderSharedVideo && !renderVideo
|
||||
&& <View style = { styles.avatarContainer }>
|
||||
<Avatar
|
||||
participantId = { this.props.participantId }
|
||||
|
||||
@@ -371,6 +371,7 @@ function _localParticipantLeft({ dispatch }, next, action) {
|
||||
function _maybePlaySounds({ getState, dispatch }, action) {
|
||||
const state = getState();
|
||||
const { startAudioMuted, disableJoinLeaveSounds } = state['features/base/config'];
|
||||
const { soundsParticipantJoined: joinSound, soundsParticipantLeft: leftSound } = state['features/base/settings'];
|
||||
|
||||
// If we have join/leave sounds disabled, don't play anything.
|
||||
if (disableJoinLeaveSounds) {
|
||||
@@ -387,13 +388,16 @@ function _maybePlaySounds({ getState, dispatch }, action) {
|
||||
const { isReplacing, isReplaced } = action.participant;
|
||||
|
||||
if (action.type === PARTICIPANT_JOINED) {
|
||||
if (!joinSound) {
|
||||
return;
|
||||
}
|
||||
const { presence } = action.participant;
|
||||
|
||||
// The sounds for the poltergeist are handled by features/invite.
|
||||
if (presence !== INVITED && presence !== CALLING && !isReplacing) {
|
||||
dispatch(playSound(PARTICIPANT_JOINED_SOUND_ID));
|
||||
}
|
||||
} else if (action.type === PARTICIPANT_LEFT && !isReplaced) {
|
||||
} else if (action.type === PARTICIPANT_LEFT && !isReplaced && leftSound) {
|
||||
dispatch(playSound(PARTICIPANT_LEFT_SOUND_ID));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,6 +27,11 @@ type Props = {
|
||||
*/
|
||||
headerLabelKey: ?string,
|
||||
|
||||
/**
|
||||
* True if the header with navigation should be hidden, false otherwise.
|
||||
*/
|
||||
hideHeaderWithNavigation?: boolean,
|
||||
|
||||
/**
|
||||
* Callback to be invoked on pressing the back button.
|
||||
*/
|
||||
@@ -48,17 +53,18 @@ class HeaderWithNavigation extends Component<Props> {
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { onPressBack, onPressForward } = this.props;
|
||||
const { hideHeaderWithNavigation, onPressBack, onPressForward } = this.props;
|
||||
|
||||
return (
|
||||
<Header>
|
||||
{ onPressBack && <BackButton onPress = { onPressBack } /> }
|
||||
<HeaderLabel labelKey = { this.props.headerLabelKey } />
|
||||
{ onPressForward && <ForwardButton
|
||||
disabled = { this.props.forwardDisabled }
|
||||
labelKey = { this.props.forwardLabelKey }
|
||||
onPress = { onPressForward } /> }
|
||||
</Header>
|
||||
!hideHeaderWithNavigation
|
||||
&& <Header>
|
||||
{ onPressBack && <BackButton onPress = { onPressBack } /> }
|
||||
<HeaderLabel labelKey = { this.props.headerLabelKey } />
|
||||
{ onPressForward && <ForwardButton
|
||||
disabled = { this.props.forwardDisabled }
|
||||
labelKey = { this.props.forwardLabelKey }
|
||||
onPress = { onPressForward } /> }
|
||||
</Header>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { isVpaasMeeting } from '../../../../billing-counter/functions';
|
||||
import { isVpaasMeeting } from '../../../../jaas/functions';
|
||||
import { translate } from '../../../i18n';
|
||||
import { connect } from '../../../redux';
|
||||
|
||||
|
||||
@@ -27,6 +27,10 @@ const DEFAULT_STATE = {
|
||||
micDeviceId: undefined,
|
||||
serverURL: undefined,
|
||||
hideShareAudioHelper: false,
|
||||
soundsIncomingMessage: true,
|
||||
soundsParticipantJoined: true,
|
||||
soundsParticipantLeft: true,
|
||||
soundsTalkWhileMuted: true,
|
||||
startAudioOnly: false,
|
||||
startWithAudioMuted: false,
|
||||
startWithVideoMuted: false,
|
||||
|
||||
@@ -227,7 +227,7 @@ export function noDataFromSource(track) {
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function showNoDataFromSourceVideoError(jitsiTrack) {
|
||||
return (dispatch, getState) => {
|
||||
return async (dispatch, getState) => {
|
||||
let notificationInfo;
|
||||
|
||||
const track = getTrackByJitsiTrack(getState()['features/base/tracks'], jitsiTrack);
|
||||
@@ -239,12 +239,11 @@ export function showNoDataFromSourceVideoError(jitsiTrack) {
|
||||
if (track.isReceivingData) {
|
||||
notificationInfo = undefined;
|
||||
} else {
|
||||
const notificationAction = showErrorNotification({
|
||||
const notificationAction = await dispatch(showErrorNotification({
|
||||
descriptionKey: 'dialog.cameraNotSendingData',
|
||||
titleKey: 'dialog.cameraNotSendingDataTitle'
|
||||
});
|
||||
}));
|
||||
|
||||
dispatch(notificationAction);
|
||||
notificationInfo = {
|
||||
uid: notificationAction.uid
|
||||
};
|
||||
@@ -362,7 +361,7 @@ function replaceStoredTracks(oldTrack, newTrack) {
|
||||
* @returns {{ type: TRACK_ADDED, track: Track }}
|
||||
*/
|
||||
export function trackAdded(track) {
|
||||
return (dispatch, getState) => {
|
||||
return async (dispatch, getState) => {
|
||||
track.on(
|
||||
JitsiTrackEvents.TRACK_MUTE_CHANGED,
|
||||
() => dispatch(trackMutedChanged(track)));
|
||||
@@ -389,12 +388,10 @@ export function trackAdded(track) {
|
||||
track.on(JitsiTrackEvents.NO_DATA_FROM_SOURCE, () => dispatch(noDataFromSource({ jitsiTrack: track })));
|
||||
if (!isReceivingData) {
|
||||
if (mediaType === MEDIA_TYPE.AUDIO) {
|
||||
const notificationAction = showNotification({
|
||||
const notificationAction = await dispatch(showNotification({
|
||||
descriptionKey: 'dialog.micNotSendingData',
|
||||
titleKey: 'dialog.micNotSendingDataTitle'
|
||||
});
|
||||
|
||||
dispatch(notificationAction);
|
||||
}));
|
||||
|
||||
// Set the notification ID so that other parts of the application know that this was
|
||||
// displayed in the context of the current device.
|
||||
|
||||
@@ -18,6 +18,7 @@ export const colors = {
|
||||
primary08: '#99BBF3',
|
||||
primary09: '#CCDDF9',
|
||||
|
||||
surface00: '#111111',
|
||||
surface01: '#040404',
|
||||
surface02: '#141414',
|
||||
surface03: '#292929',
|
||||
@@ -29,11 +30,13 @@ export const colors = {
|
||||
surface09: '#C2C2C2',
|
||||
surface10: '#E0E0E0',
|
||||
surface11: '#FFF',
|
||||
surface12: '#AAAAAA',
|
||||
|
||||
success04: '#189B55',
|
||||
success05: '#1EC26A',
|
||||
|
||||
warning05: '#F8AE1A'
|
||||
warning05: '#F8AE1A',
|
||||
warning06: '#ED9E1B'
|
||||
};
|
||||
|
||||
// Mapping between the token used and the color
|
||||
@@ -108,6 +111,9 @@ export const colorMap = {
|
||||
// Disabled state for danger buttons
|
||||
actionDangerDisabled: 'error03',
|
||||
|
||||
// Bottom sheet background
|
||||
bottomSheet: 'surface00',
|
||||
|
||||
// Primary text – default color for body copy & headers
|
||||
text01: 'surface11',
|
||||
|
||||
@@ -117,6 +123,9 @@ export const colorMap = {
|
||||
// Tertiary text with low contrast – placeholders, disabled actions, label for disabled buttons
|
||||
text03: 'surface07',
|
||||
|
||||
// Text for bottom sheet items
|
||||
text04: 'surface12',
|
||||
|
||||
// error messages
|
||||
textError: 'error06',
|
||||
|
||||
@@ -148,6 +157,9 @@ export const colorMap = {
|
||||
// Background for high-contrast input fields
|
||||
field02: 'surface11',
|
||||
|
||||
// Color for the section divider
|
||||
dividerColor: 'surface12',
|
||||
|
||||
// Background for high-contrast input fields on hover
|
||||
field02Hover: 'primary09',
|
||||
|
||||
@@ -197,20 +209,24 @@ export const colorMap = {
|
||||
success02: 'success05',
|
||||
|
||||
// Color for warning messages applied to icons, borders & backgrounds
|
||||
warning01: 'warning05'
|
||||
warning01: 'warning05',
|
||||
|
||||
// Color for indicating a raised hand
|
||||
warning02: 'warning06'
|
||||
};
|
||||
|
||||
|
||||
export const font = {
|
||||
weightRegular: 400,
|
||||
weightSemiBold: 600
|
||||
weightRegular: '400',
|
||||
weightSemiBold: '600'
|
||||
};
|
||||
|
||||
export const shape = {
|
||||
borderRadius: 6
|
||||
borderRadius: 6,
|
||||
boxShadow: 'inset 0px -1px 0px rgba(255, 255, 255, 0.15)'
|
||||
};
|
||||
|
||||
export const spacing = [ 0, 4, 8, 16, 24, 32, 40, 48, 56 ];
|
||||
export const spacing = [ 0, 4, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80 ];
|
||||
|
||||
export const typography = {
|
||||
labelRegular: {
|
||||
|
||||
@@ -45,17 +45,18 @@ const _URI_PATH_PATTERN = '([^?#]*)';
|
||||
export const URI_PROTOCOL_PATTERN = '^([a-z][a-z0-9\\.\\+-]*:)';
|
||||
|
||||
/**
|
||||
* Excludes/removes certain characters from a specific room (name) which are
|
||||
* incompatible with Jitsi Meet on the client and/or server sides.
|
||||
* Excludes/removes certain characters from a specific path part which are
|
||||
* incompatible with Jitsi Meet on the client and/or server sides. The main
|
||||
* use case for this method is to clean up the room name and the tenant.
|
||||
*
|
||||
* @param {?string} room - The room (name) to fix.
|
||||
* @param {?string} pathPart - The path part to fix.
|
||||
* @private
|
||||
* @returns {?string}
|
||||
*/
|
||||
function _fixRoom(room: ?string) {
|
||||
return room
|
||||
? room.replace(new RegExp(_ROOM_EXCLUDE_PATTERN, 'g'), '')
|
||||
: room;
|
||||
function _fixPathPart(pathPart: ?string) {
|
||||
return pathPart
|
||||
? pathPart.replace(new RegExp(_ROOM_EXCLUDE_PATTERN, 'g'), '')
|
||||
: pathPart;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -335,6 +336,11 @@ export function parseURIString(uri: ?string) {
|
||||
|
||||
const obj = parseStandardURIString(_fixURIStringScheme(uri));
|
||||
|
||||
// XXX While the components/segments of pathname are URI encoded, Jitsi Meet
|
||||
// on the client and/or server sides still don't support certain characters.
|
||||
obj.pathname = obj.pathname.split('/').map(pathPart => _fixPathPart(pathPart))
|
||||
.join('/');
|
||||
|
||||
// Add the properties that are specific to a Jitsi Meet resource (location)
|
||||
// such as contextRoot, room:
|
||||
|
||||
@@ -344,24 +350,9 @@ export function parseURIString(uri: ?string) {
|
||||
// The room (name) is the last component/segment of pathname.
|
||||
const { pathname } = obj;
|
||||
|
||||
// XXX While the components/segments of pathname are URI encoded, Jitsi Meet
|
||||
// on the client and/or server sides still don't support certain characters.
|
||||
const contextRootEndIndex = pathname.lastIndexOf('/');
|
||||
let room = pathname.substring(contextRootEndIndex + 1) || undefined;
|
||||
|
||||
if (room) {
|
||||
const fixedRoom = _fixRoom(room);
|
||||
|
||||
if (fixedRoom !== room) {
|
||||
room = fixedRoom;
|
||||
|
||||
// XXX Drive fixedRoom into pathname (because room is derived from
|
||||
// pathname).
|
||||
obj.pathname
|
||||
= pathname.substring(0, contextRootEndIndex + 1) + (room || '');
|
||||
}
|
||||
}
|
||||
obj.room = room;
|
||||
obj.room = pathname.substring(contextRootEndIndex + 1) || undefined;
|
||||
|
||||
if (contextRootEndIndex > 1) {
|
||||
// The part of the pathname from the beginning to the room name is the tenant.
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
/**
|
||||
* Action used to store the flag signaling the endpoint has been counted.
|
||||
*/
|
||||
export const SET_ENDPOINT_COUNTED = 'SET_ENDPOINT_COUNTED';
|
||||
@@ -1,42 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import { SET_ENDPOINT_COUNTED } from './actionTypes';
|
||||
import { extractVpaasTenantFromPath, getBillingId, sendCountRequest } from './functions';
|
||||
|
||||
/**
|
||||
* Sends a billing count request when needed.
|
||||
*
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function countEndpoint() {
|
||||
return function(dispatch: Function, getState: Function) {
|
||||
const state = getState();
|
||||
const baseUrl = state['features/base/config'].billingCounterUrl;
|
||||
const jwt = state['features/base/jwt'].jwt;
|
||||
const tenant = extractVpaasTenantFromPath(state['features/base/connection'].locationURL.pathname);
|
||||
const shouldSendRequest = Boolean(baseUrl && jwt && tenant);
|
||||
|
||||
if (shouldSendRequest) {
|
||||
const billingId = getBillingId();
|
||||
|
||||
sendCountRequest({
|
||||
baseUrl,
|
||||
billingId,
|
||||
jwt,
|
||||
tenant
|
||||
});
|
||||
dispatch(setEndpointCounted());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Action used to mark the endpoint as counted.
|
||||
*
|
||||
* @returns {Object}
|
||||
*/
|
||||
function setEndpointCounted() {
|
||||
return {
|
||||
type: SET_ENDPOINT_COUNTED
|
||||
};
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
/**
|
||||
* The key for the billing id stored in localStorage.
|
||||
*/
|
||||
export const BILLING_ID = 'jitsiMeetId';
|
||||
|
||||
/**
|
||||
* The prefix for the vpaas tenant.
|
||||
*/
|
||||
export const VPAAS_TENANT_PREFIX = 'vpaas-magic-cookie';
|
||||
@@ -1,116 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import { jitsiLocalStorage } from '@jitsi/js-utils';
|
||||
import uuid from 'uuid';
|
||||
|
||||
import { BILLING_ID, VPAAS_TENANT_PREFIX } from './constants';
|
||||
import logger from './logger';
|
||||
|
||||
/**
|
||||
* Returns the full vpaas tenant if available, given a path.
|
||||
*
|
||||
* @param {string} path - The meeting url path.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function extractVpaasTenantFromPath(path: string) {
|
||||
const [ , tenant ] = path.split('/');
|
||||
|
||||
if (tenant.startsWith(VPAAS_TENANT_PREFIX)) {
|
||||
return tenant;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the vpaas tenant.
|
||||
*
|
||||
* @param {Object} state - The global state.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getVpaasTenant(state: Object) {
|
||||
return extractVpaasTenantFromPath(state['features/base/connection'].locationURL.pathname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current meeting is a vpaas one.
|
||||
*
|
||||
* @param {Object} state - The state of the app.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isVpaasMeeting(state: Object) {
|
||||
const { billingCounterUrl, iAmRecorder, iAmSipGateway } = state['features/base/config'];
|
||||
const { jwt } = state['features/base/jwt'];
|
||||
|
||||
const isAllowed = iAmRecorder || iAmSipGateway || Boolean(jwt);
|
||||
|
||||
return Boolean(
|
||||
billingCounterUrl
|
||||
&& extractVpaasTenantFromPath(
|
||||
state['features/base/connection'].locationURL.pathname)
|
||||
&& isAllowed
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a billing counter request.
|
||||
*
|
||||
* @param {Object} reqData - The request info.
|
||||
* @param {string} reqData.baseUrl - The base url for the request.
|
||||
* @param {string} billingId - The unique id of the client.
|
||||
* @param {string} jwt - The JWT token.
|
||||
* @param {string} tenat - The client tenant.
|
||||
* @returns {void}
|
||||
*/
|
||||
export async function sendCountRequest({ baseUrl, billingId, jwt, tenant }: {
|
||||
baseUrl: string,
|
||||
billingId: string,
|
||||
jwt: string,
|
||||
tenant: string
|
||||
}) {
|
||||
const fullUrl = `${baseUrl}/${encodeURIComponent(tenant)}/${billingId}`;
|
||||
const headers = {
|
||||
'Authorization': `Bearer ${jwt}`
|
||||
};
|
||||
|
||||
try {
|
||||
const res = await fetch(fullUrl, {
|
||||
method: 'GET',
|
||||
headers
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
logger.error('Status error:', res.status);
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error('Could not send request', err);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the stored billing id (or generates a new one if none is present).
|
||||
*
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getBillingId() {
|
||||
let billingId = jitsiLocalStorage.getItem(BILLING_ID);
|
||||
|
||||
if (!billingId) {
|
||||
billingId = uuid.v4();
|
||||
jitsiLocalStorage.setItem(BILLING_ID, billingId);
|
||||
}
|
||||
|
||||
return billingId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the billing id for vpaas meetings.
|
||||
*
|
||||
* @param {Object} state - The state of the app.
|
||||
* @returns {string | undefined}
|
||||
*/
|
||||
export function getVpaasBillingId(state: Object) {
|
||||
if (isVpaasMeeting(state)) {
|
||||
return getBillingId();
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import { ReducerRegistry } from '../base/redux';
|
||||
|
||||
import {
|
||||
SET_ENDPOINT_COUNTED
|
||||
} from './actionTypes';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
endpointCounted: false
|
||||
};
|
||||
|
||||
/**
|
||||
* Listen for actions that mutate the billing-counter state
|
||||
*/
|
||||
ReducerRegistry.register(
|
||||
'features/billing-counter', (state = DEFAULT_STATE, action) => {
|
||||
switch (action.type) {
|
||||
|
||||
case SET_ENDPOINT_COUNTED: {
|
||||
return {
|
||||
...state,
|
||||
endpointCounted: true
|
||||
};
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
},
|
||||
);
|
||||
@@ -1,5 +1,8 @@
|
||||
// @flow
|
||||
|
||||
import { batch } from 'react-redux';
|
||||
|
||||
import { ENDPOINT_REACTION_NAME } from '../../../modules/API/constants';
|
||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../base/app';
|
||||
import {
|
||||
CONFERENCE_JOINED,
|
||||
@@ -19,7 +22,16 @@ import {
|
||||
import { MiddlewareRegistry, StateListenerRegistry } from '../base/redux';
|
||||
import { playSound, registerSound, unregisterSound } from '../base/sounds';
|
||||
import { openDisplayNamePrompt } from '../display-name';
|
||||
import { ADD_REACTION_MESSAGE } from '../reactions/actionTypes';
|
||||
import { pushReactions } from '../reactions/actions.any';
|
||||
import { getReactionMessageFromBuffer } from '../reactions/functions.any';
|
||||
import { endpointMessageReceived } from '../subtitles';
|
||||
import { showToolbox } from '../toolbox/actions';
|
||||
import {
|
||||
hideToolbox,
|
||||
setToolboxTimeout,
|
||||
setToolboxVisible
|
||||
} from '../toolbox/actions.web';
|
||||
|
||||
import { ADD_MESSAGE, SEND_MESSAGE, OPEN_CHAT, CLOSE_CHAT } from './actionTypes';
|
||||
import { addMessage, clearMessages } from './actions';
|
||||
@@ -143,6 +155,15 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case ADD_REACTION_MESSAGE: {
|
||||
_handleReceivedMessage(store, {
|
||||
id: localParticipant.id,
|
||||
message: action.message,
|
||||
privateMessage: false,
|
||||
timestamp: Date.now()
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
||||
@@ -189,7 +210,6 @@ StateListenerRegistry.register(
|
||||
* @returns {void}
|
||||
*/
|
||||
function _addChatMsgListener(conference, store) {
|
||||
|
||||
if (store.getState()['features/base/config'].iAmRecorder) {
|
||||
// We don't register anything on web if we are in iAmRecorder mode
|
||||
return;
|
||||
@@ -219,6 +239,34 @@ function _addChatMsgListener(conference, store) {
|
||||
}
|
||||
);
|
||||
|
||||
conference.on(
|
||||
JitsiConferenceEvents.ENDPOINT_MESSAGE_RECEIVED,
|
||||
(...args) => {
|
||||
store.dispatch(endpointMessageReceived(...args));
|
||||
|
||||
if (args && args.length >= 2) {
|
||||
const [ { _id }, eventData ] = args;
|
||||
|
||||
if (eventData.name === ENDPOINT_REACTION_NAME) {
|
||||
batch(() => {
|
||||
store.dispatch(setToolboxVisible(true));
|
||||
store.dispatch(setToolboxTimeout(
|
||||
() => store.dispatch(hideToolbox()),
|
||||
5000)
|
||||
);
|
||||
store.dispatch(pushReactions(eventData.reactions));
|
||||
});
|
||||
|
||||
_handleReceivedMessage(store, {
|
||||
id: _id,
|
||||
message: getReactionMessageFromBuffer(eventData.reactions),
|
||||
privateMessage: false,
|
||||
timestamp: eventData.timestamp
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
conference.on(
|
||||
JitsiConferenceEvents.CONFERENCE_ERROR, (errorType, error) => {
|
||||
errorType === JitsiConferenceErrors.CHAT_ERROR && _handleChatError(store, error);
|
||||
@@ -247,14 +295,20 @@ function _handleChatError({ dispatch }, error) {
|
||||
*
|
||||
* @param {Store} store - The Redux store.
|
||||
* @param {Object} message - The message object.
|
||||
* @param {boolean} shouldPlaySound - Whether or not to play the incoming message sound.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _handleReceivedMessage({ dispatch, getState }, { id, message, privateMessage, timestamp }) {
|
||||
function _handleReceivedMessage({ dispatch, getState },
|
||||
{ id, message, privateMessage, timestamp },
|
||||
shouldPlaySound = true
|
||||
) {
|
||||
// Logic for all platforms:
|
||||
const state = getState();
|
||||
const { isOpen: isChatOpen } = state['features/chat'];
|
||||
const { disableIncomingMessageSound } = state['features/base/config'];
|
||||
const { soundsIncomingMessage: soundEnabled } = state['features/base/settings'];
|
||||
|
||||
if (!isChatOpen) {
|
||||
if (!disableIncomingMessageSound && soundEnabled && shouldPlaySound && !isChatOpen) {
|
||||
dispatch(playSound(INCOMING_MSG_SOUND_ID));
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import { translate } from '../../base/i18n';
|
||||
import { Icon, IconClose } from '../../base/icons';
|
||||
import { browser } from '../../base/lib-jitsi-meet';
|
||||
import { connect } from '../../base/redux';
|
||||
import { isVpaasMeeting } from '../../billing-counter/functions';
|
||||
import { isVpaasMeeting } from '../../jaas/functions';
|
||||
import logger from '../logger';
|
||||
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ import { AddPeopleDialog, CalleeInfoContainer } from '../../../invite';
|
||||
import { LargeVideo } from '../../../large-video';
|
||||
import { KnockingParticipantList } from '../../../lobby';
|
||||
import { BackButtonRegistry } from '../../../mobile/back-button';
|
||||
import { ParticipantsPane } from '../../../participants-pane/components/native';
|
||||
import { Captions } from '../../../subtitles';
|
||||
import { setToolboxVisible } from '../../../toolbox/actions';
|
||||
import { Toolbox } from '../../../toolbox/components/native';
|
||||
@@ -71,6 +72,11 @@ type Props = AbstractProps & {
|
||||
*/
|
||||
_fullscreenEnabled: boolean,
|
||||
|
||||
/**
|
||||
* The indicator which determines if the participants pane is open.
|
||||
*/
|
||||
_isParticipantsPaneOpen: boolean,
|
||||
|
||||
/**
|
||||
* The ID of the participant currently on stage (if any)
|
||||
*/
|
||||
@@ -237,6 +243,7 @@ class Conference extends AbstractConference<Props, *> {
|
||||
_renderContent() {
|
||||
const {
|
||||
_connecting,
|
||||
_isParticipantsPaneOpen,
|
||||
_largeVideoParticipantId,
|
||||
_reducedUI,
|
||||
_shouldDisplayTileView
|
||||
@@ -296,11 +303,14 @@ class Conference extends AbstractConference<Props, *> {
|
||||
</SafeAreaView>
|
||||
|
||||
<TestConnectionInfo />
|
||||
|
||||
{ this._renderConferenceNotification() }
|
||||
|
||||
{ this._renderConferenceModals() }
|
||||
|
||||
{_shouldDisplayTileView && <Toolbox />}
|
||||
|
||||
{ _isParticipantsPaneOpen && <ParticipantsPane /> }
|
||||
|
||||
</>
|
||||
);
|
||||
}
|
||||
@@ -391,6 +401,7 @@ function _mapStateToProps(state) {
|
||||
membersOnly,
|
||||
leaving
|
||||
} = state['features/base/conference'];
|
||||
const { isOpen } = state['features/participants-pane'];
|
||||
const { aspectRatio, reducedUI } = state['features/base/responsive-ui'];
|
||||
|
||||
// XXX There is a window of time between the successful establishment of the
|
||||
@@ -412,6 +423,7 @@ function _mapStateToProps(state) {
|
||||
_connecting: Boolean(connecting_),
|
||||
_filmstripVisible: isFilmstripVisible(state),
|
||||
_fullscreenEnabled: getFeatureFlag(state, FULLSCREEN_ENABLED, true),
|
||||
_isParticipantsPaneOpen: isOpen,
|
||||
_largeVideoParticipantId: state['features/large-video'].participantId,
|
||||
_pictureInPictureEnabled: getFeatureFlag(state, PIP_ENABLED),
|
||||
_reducedUI: reducedUI,
|
||||
|
||||
@@ -15,7 +15,7 @@ import { Filmstrip } from '../../../filmstrip';
|
||||
import { CalleeInfoContainer } from '../../../invite';
|
||||
import { LargeVideo } from '../../../large-video';
|
||||
import { KnockingParticipantList, LobbyScreen } from '../../../lobby';
|
||||
import { ParticipantsPane } from '../../../participants-pane/components';
|
||||
import { ParticipantsPane } from '../../../participants-pane/components/web';
|
||||
import { getParticipantsPaneOpen } from '../../../participants-pane/functions';
|
||||
import { Prejoin, isPrejoinPageVisible } from '../../../prejoin';
|
||||
import { fullScreenChanged, showToolbox } from '../../../toolbox/actions.web';
|
||||
|
||||
@@ -8,7 +8,7 @@ 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 { getSessionStatusToShow, RecordingLabel } from '../../../recording';
|
||||
import { isToolboxVisible } from '../../../toolbox/functions.web';
|
||||
import { TranscribingLabel } from '../../../transcribing';
|
||||
import { VideoQualityLabel } from '../../../video-quality';
|
||||
@@ -38,6 +38,11 @@ type Props = {
|
||||
*/
|
||||
_hideConferenceTimer: boolean,
|
||||
|
||||
/**
|
||||
* Whether the recording label should be shown or not.
|
||||
*/
|
||||
_hideRecordingLabel: boolean,
|
||||
|
||||
/**
|
||||
* Whether the participant count should be shown or not.
|
||||
*/
|
||||
@@ -52,7 +57,20 @@ type Props = {
|
||||
/**
|
||||
* Indicates whether the component should be visible or not.
|
||||
*/
|
||||
_visible: boolean
|
||||
_visible: boolean,
|
||||
|
||||
/**
|
||||
* Whether or not the recording label is visible.
|
||||
*/
|
||||
_recordingLabel: boolean
|
||||
};
|
||||
|
||||
const getLeftMargin = () => {
|
||||
const subjectContainerWidth = document.getElementById('subject-container')?.clientWidth ?? 0;
|
||||
const recContainerWidth = document.getElementById('rec-container')?.clientWidth ?? 0;
|
||||
const subjectDetailsContainer = document.getElementById('subject-details-container')?.clientWidth ?? 0;
|
||||
|
||||
return (subjectContainerWidth - recContainerWidth - subjectDetailsContainer) / 2;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -66,29 +84,53 @@ function ConferenceInfo(props: Props) {
|
||||
_hideConferenceNameAndTimer,
|
||||
_hideConferenceTimer,
|
||||
_showParticipantCount,
|
||||
_hideRecordingLabel,
|
||||
_subject,
|
||||
_fullWidth,
|
||||
_visible
|
||||
_visible,
|
||||
_recordingLabel
|
||||
} = 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>
|
||||
<div className = { `subject ${_recordingLabel ? 'recording' : ''} ${_visible ? 'visible' : ''}` }>
|
||||
<div
|
||||
className = { `subject-info-container${_fullWidth ? ' subject-info-container--full-width' : ''}` }
|
||||
id = 'subject-container'>
|
||||
{!_hideRecordingLabel && <div
|
||||
className = 'show-always'
|
||||
id = 'rec-container'
|
||||
// eslint-disable-next-line react-native/no-inline-styles
|
||||
style = {{
|
||||
marginLeft: !_recordingLabel || _visible ? 0 : getLeftMargin()
|
||||
}}>
|
||||
<RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
|
||||
<RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
|
||||
<LocalRecordingLabel />
|
||||
</div>
|
||||
}
|
||||
{ _showParticipantCount && <ParticipantsCount /> }
|
||||
<E2EELabel />
|
||||
<RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
|
||||
<RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
|
||||
<LocalRecordingLabel />
|
||||
<TranscribingLabel />
|
||||
<VideoQualityLabel />
|
||||
<InsecureRoomNameLabel />
|
||||
<div
|
||||
className = 'subject-details-container'
|
||||
id = 'subject-details-container'>
|
||||
{
|
||||
!_hideConferenceNameAndTimer
|
||||
&& <div className = 'subject-info'>
|
||||
{ _subject && <span className = 'subject-text'>{ _subject }</span>}
|
||||
{ !_hideConferenceTimer && <ConferenceTimer /> }
|
||||
</div>
|
||||
}
|
||||
{ _showParticipantCount && <ParticipantsCount /> }
|
||||
<E2EELabel />
|
||||
{_hideRecordingLabel && (
|
||||
<>
|
||||
<RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
|
||||
<RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
|
||||
<LocalRecordingLabel />
|
||||
</>
|
||||
)}
|
||||
<TranscribingLabel />
|
||||
<VideoQualityLabel />
|
||||
<InsecureRoomNameLabel />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -109,16 +151,32 @@ function ConferenceInfo(props: Props) {
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
const participantCount = getParticipantCount(state);
|
||||
const { hideConferenceTimer, hideConferenceSubject, hideParticipantsStats } = state['features/base/config'];
|
||||
const {
|
||||
hideConferenceTimer,
|
||||
hideConferenceSubject,
|
||||
hideParticipantsStats,
|
||||
hideRecordingLabel,
|
||||
iAmRecorder
|
||||
} = state['features/base/config'];
|
||||
const { clientWidth } = state['features/base/responsive-ui'];
|
||||
|
||||
const shouldHideRecordingLabel = hideRecordingLabel || iAmRecorder;
|
||||
const fileRecordingStatus = getSessionStatusToShow(state, JitsiRecordingConstants.mode.FILE);
|
||||
const streamRecordingStatus = getSessionStatusToShow(state, JitsiRecordingConstants.mode.STREAM);
|
||||
const isFileRecording = fileRecordingStatus ? fileRecordingStatus !== JitsiRecordingConstants.status.OFF : false;
|
||||
const isStreamRecording = streamRecordingStatus
|
||||
? streamRecordingStatus !== JitsiRecordingConstants.status.OFF : false;
|
||||
const { isEngaged } = state['features/local-recording'];
|
||||
|
||||
return {
|
||||
_hideConferenceNameAndTimer: clientWidth < 300,
|
||||
_hideConferenceTimer: Boolean(hideConferenceTimer),
|
||||
_hideRecordingLabel: shouldHideRecordingLabel,
|
||||
_fullWidth: state['features/video-layout'].tileViewEnabled,
|
||||
_showParticipantCount: participantCount > 2 && !hideParticipantsStats,
|
||||
_subject: hideConferenceSubject ? '' : getConferenceName(state),
|
||||
_visible: isToolboxVisible(state)
|
||||
_visible: isToolboxVisible(state),
|
||||
_recordingLabel: (isFileRecording || isStreamRecording || isEngaged) && !shouldHideRecordingLabel
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,30 +1,9 @@
|
||||
import { getName } from '../app/functions.web';
|
||||
import { isSuboptimalBrowser } from '../base/environment';
|
||||
import { translateToHTML } from '../base/i18n';
|
||||
import { getLocalParticipant } from '../base/participants';
|
||||
import { toState } from '../base/redux';
|
||||
import { getBackendSafePath, getJitsiMeetGlobalNS } from '../base/util';
|
||||
import { getVpaasBillingId } from '../billing-counter/functions';
|
||||
import { showWarningNotification } from '../notifications';
|
||||
import { createRnnoiseProcessor } from '../stream-effects/rnnoise';
|
||||
|
||||
export * from './functions.any';
|
||||
|
||||
/**
|
||||
* Returns the result of getWiFiStats from the global NS or does nothing
|
||||
(returns empty result).
|
||||
* Fixes a concurrency problem where we need to pass a function when creating
|
||||
* a JitsiConference, but that method is added to the context later.
|
||||
*
|
||||
* @returns {Promise}
|
||||
* @private
|
||||
*/
|
||||
const getWiFiStatsMethod = () => {
|
||||
const gloabalNS = getJitsiMeetGlobalNS();
|
||||
|
||||
return gloabalNS.getWiFiStats ? gloabalNS.getWiFiStats() : Promise.resolve('{}');
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows the suboptimal experience notification if needed.
|
||||
*
|
||||
@@ -50,49 +29,3 @@ export function maybeShowSuboptimalExperienceNotification(dispatch, t) {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an object aggregating the conference options.
|
||||
*
|
||||
* @param {Object|Function} stateful - The redux store state.
|
||||
* @returns {Object} - Options object.
|
||||
*/
|
||||
export function getConferenceOptions(stateful) {
|
||||
const state = toState(stateful);
|
||||
|
||||
const options = state['features/base/config'];
|
||||
const { locationURL } = state['features/base/connection'];
|
||||
const { tenant } = state['features/base/jwt'];
|
||||
|
||||
const { email, name: nick } = getLocalParticipant(state);
|
||||
|
||||
if (tenant) {
|
||||
options.siteID = tenant;
|
||||
}
|
||||
|
||||
if (options.enableDisplayNameInStats && nick) {
|
||||
options.statisticsDisplayName = nick;
|
||||
}
|
||||
|
||||
if (options.enableEmailInStats && email) {
|
||||
options.statisticsId = email;
|
||||
}
|
||||
|
||||
if (locationURL) {
|
||||
options.confID = `${locationURL.host}${getBackendSafePath(locationURL.pathname)}`;
|
||||
}
|
||||
|
||||
options.applicationName = getName();
|
||||
options.getWiFiStatsMethod = getWiFiStatsMethod;
|
||||
options.createVADProcessor = createRnnoiseProcessor;
|
||||
options.billingId = getVpaasBillingId(state);
|
||||
|
||||
// Disable CallStats, if requessted.
|
||||
if (options.disableThirdPartyRequests) {
|
||||
delete options.callStatsID;
|
||||
delete options.callStatsSecret;
|
||||
delete options.getWiFiStatsMethod;
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { isMobileBrowser } from '../base/environment/utils';
|
||||
import { Platform } from '../base/react';
|
||||
import { URI_PROTOCOL_PATTERN } from '../base/util';
|
||||
import { isVpaasMeeting } from '../billing-counter/functions';
|
||||
import { isVpaasMeeting } from '../jaas/functions';
|
||||
|
||||
import {
|
||||
DeepLinkingDesktopPage,
|
||||
@@ -58,7 +58,7 @@ export function getDeepLinkingPage(state) {
|
||||
if (launchInWeb
|
||||
|| !room
|
||||
|| state['features/base/config'].disableDeepLinking
|
||||
|| (isVpaasMeeting(state) && !appScheme)) {
|
||||
|| (isVpaasMeeting(state) && (!appScheme || appScheme === 'com.8x8.meet'))) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
getDeviceIdByLabel,
|
||||
groupDevicesByKind,
|
||||
setAudioInputDeviceAndUpdateSettings,
|
||||
setAudioOutputDeviceId,
|
||||
setAudioOutputDevice,
|
||||
setVideoInputDeviceAndUpdateSettings
|
||||
} from '../base/devices';
|
||||
import { isIosMobileBrowser } from '../base/environment/utils';
|
||||
@@ -189,12 +189,11 @@ export function processExternalDeviceRequest( // eslint-disable-line max-params
|
||||
|
||||
if (deviceId) {
|
||||
switch (device.kind) {
|
||||
case 'audioinput': {
|
||||
case 'audioinput':
|
||||
dispatch(setAudioInputDeviceAndUpdateSettings(deviceId));
|
||||
break;
|
||||
}
|
||||
case 'audiooutput':
|
||||
setAudioOutputDeviceId(deviceId, dispatch);
|
||||
dispatch(setAudioOutputDevice(deviceId));
|
||||
break;
|
||||
case 'videoinput':
|
||||
dispatch(setVideoInputDeviceAndUpdateSettings(deviceId));
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
// @flow
|
||||
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import React from 'react';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The name to be displayed within the badge.
|
||||
*/
|
||||
name: string
|
||||
}
|
||||
|
||||
const useStyles = makeStyles(theme => {
|
||||
return {
|
||||
badge: {
|
||||
background: 'rgba(0, 0, 0, 0.6)',
|
||||
borderRadius: '3px',
|
||||
color: theme.palette.text01,
|
||||
maxWidth: '50%',
|
||||
overflow: 'hidden',
|
||||
padding: '2px 16px',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap'
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Component that displays a name badge.
|
||||
*
|
||||
* @param {Props} props - The props of the component.
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
const DisplayNameBadge = ({ name }: Props) => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<div className = { classes.badge }>
|
||||
{name}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DisplayNameBadge;
|
||||
@@ -0,0 +1,60 @@
|
||||
// @flow
|
||||
|
||||
import { makeStyles } from '@material-ui/core/styles';
|
||||
import React from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { getLocalParticipant } from '../../../base/participants';
|
||||
import { withPixelLineHeight } from '../../../base/styles/functions.web';
|
||||
import { getLargeVideoParticipant } from '../../../large-video/functions';
|
||||
import { isToolboxVisible } from '../../../toolbox/functions.web';
|
||||
import { isLayoutTileView } from '../../../video-layout';
|
||||
|
||||
import DisplayNameBadge from './DisplayNameBadge';
|
||||
|
||||
const useStyles = makeStyles(theme => {
|
||||
return {
|
||||
badgeContainer: {
|
||||
...withPixelLineHeight(theme.typography.bodyShortRegularLarge),
|
||||
alignItems: 'center',
|
||||
display: 'flex',
|
||||
justifyContent: 'center',
|
||||
marginBottom: theme.spacing(2),
|
||||
transition: 'margin-bottom 0.3s'
|
||||
},
|
||||
containerElevated: {
|
||||
marginBottom: theme.spacing(7)
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Component that renders the dominant speaker's name as a badge above the toolbar in stage view.
|
||||
*
|
||||
* @returns {ReactElement|null}
|
||||
*/
|
||||
const DominantSpeakerName = () => {
|
||||
const classes = useStyles();
|
||||
const largeVideoParticipant = useSelector(getLargeVideoParticipant);
|
||||
const nameToDisplay = largeVideoParticipant?.name;
|
||||
const selectedId = largeVideoParticipant?.id;
|
||||
|
||||
const localParticipant = useSelector(getLocalParticipant);
|
||||
const localId = localParticipant?.id;
|
||||
|
||||
const isTileView = useSelector(isLayoutTileView);
|
||||
const toolboxVisible = useSelector(isToolboxVisible);
|
||||
|
||||
if (nameToDisplay && selectedId !== localId && !isTileView) {
|
||||
return (
|
||||
<div
|
||||
className = { `${classes.badgeContainer}${toolboxVisible ? '' : ` ${classes.containerElevated}`}` }>
|
||||
<DisplayNameBadge name = { nameToDisplay } />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
export default DominantSpeakerName;
|
||||
@@ -3,3 +3,4 @@
|
||||
export { default as DisplayName } from './DisplayName';
|
||||
export { default as DisplayNameLabel } from './DisplayNameLabel';
|
||||
export { default as DisplayNamePrompt } from './DisplayNamePrompt';
|
||||
export { default as DominantSpeakerName } from './DominantSpeakerName';
|
||||
|
||||
@@ -15,6 +15,15 @@ import {
|
||||
const STORE_NAME = 'features/dynamic-branding';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
|
||||
/**
|
||||
* The pool of avatar backgrounds.
|
||||
*
|
||||
* @public
|
||||
* @type {Array<string>}
|
||||
*/
|
||||
avatarBackgrounds: [],
|
||||
|
||||
/**
|
||||
* The custom background color for the LargeVideo.
|
||||
*
|
||||
@@ -112,10 +121,12 @@ ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
|
||||
didPageUrl,
|
||||
inviteDomain,
|
||||
logoClickUrl,
|
||||
logoImageUrl
|
||||
logoImageUrl,
|
||||
avatarBackgrounds
|
||||
} = action.value;
|
||||
|
||||
return {
|
||||
avatarBackgrounds,
|
||||
backgroundColor,
|
||||
backgroundImageUrl,
|
||||
defaultBranding,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
// @flow
|
||||
|
||||
import { MEDIA_TYPE, VIDEO_TYPE } from '../base/media';
|
||||
import { getLocalParticipant } from '../base/participants';
|
||||
import { StateListenerRegistry } from '../base/redux';
|
||||
import { getTrackByMediaTypeAndParticipant } from '../base/tracks';
|
||||
import { appendSuffix } from '../display-name';
|
||||
import { shouldDisplayTileView } from '../video-layout';
|
||||
|
||||
@@ -45,12 +43,7 @@ StateListenerRegistry.register(
|
||||
*/
|
||||
StateListenerRegistry.register(
|
||||
/* selector */ state => state['features/large-video'].participantId,
|
||||
/* listener */ (participantId, store) => {
|
||||
const videoTrack = getTrackByMediaTypeAndParticipant(
|
||||
store.getState()['features/base/tracks'], MEDIA_TYPE.VIDEO, participantId);
|
||||
|
||||
if (videoTrack && videoTrack.videoType === VIDEO_TYPE.CAMERA) {
|
||||
APP.API.notifyOnStageParticipantChanged(participantId);
|
||||
}
|
||||
/* listener */ participantId => {
|
||||
APP.API.notifyOnStageParticipantChanged(participantId);
|
||||
}
|
||||
);
|
||||
|
||||
@@ -4,8 +4,8 @@ import type { Dispatch } from 'redux';
|
||||
|
||||
import { FEEDBACK_REQUEST_IN_PROGRESS } from '../../../modules/UI/UIErrors';
|
||||
import { openDialog } from '../base/dialog';
|
||||
import { isVpaasMeeting } from '../billing-counter/functions';
|
||||
import { extractFqnFromPath } from '../dynamic-branding/functions';
|
||||
import { isVpaasMeeting } from '../jaas/functions';
|
||||
|
||||
import {
|
||||
CANCEL_FEEDBACK,
|
||||
|
||||
@@ -137,7 +137,7 @@ export function clickOnVideo(n: number) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the volume for a thumnail's audio.
|
||||
* Sets the volume for a thumbnail's audio.
|
||||
*
|
||||
* @param {string} participantId - The participant ID asociated with the audio.
|
||||
* @param {string} volume - The volume level.
|
||||
|
||||
@@ -6,12 +6,15 @@ import { SafeAreaView, ScrollView } from 'react-native';
|
||||
import { Platform } from '../../../base/react';
|
||||
import { connect } from '../../../base/redux';
|
||||
import { ASPECT_RATIO_NARROW } from '../../../base/responsive-ui/constants';
|
||||
import { isFilmstripVisible } from '../../functions';
|
||||
import { isFilmstripVisible, shouldRemoteVideosBeVisible } from '../../functions';
|
||||
|
||||
import LocalThumbnail from './LocalThumbnail';
|
||||
import Thumbnail from './Thumbnail';
|
||||
import styles from './styles';
|
||||
|
||||
// Immutable reference to avoid re-renders.
|
||||
const NO_REMOTE_VIDEOS = [];
|
||||
|
||||
/**
|
||||
* Filmstrip component's property types.
|
||||
*/
|
||||
@@ -167,10 +170,11 @@ class Filmstrip extends Component<Props> {
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
const { enabled, remoteParticipants } = state['features/filmstrip'];
|
||||
const showRemoteVideos = shouldRemoteVideosBeVisible(state);
|
||||
|
||||
return {
|
||||
_aspectRatio: state['features/base/responsive-ui'].aspectRatio,
|
||||
_participants: remoteParticipants,
|
||||
_participants: showRemoteVideos ? remoteParticipants : NO_REMOTE_VIDEOS,
|
||||
_visible: enabled && isFilmstripVisible(state)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,7 +13,8 @@ import {
|
||||
getParticipantCount,
|
||||
isEveryoneModerator,
|
||||
pinParticipant,
|
||||
getParticipantByIdOrUndefined
|
||||
getParticipantByIdOrUndefined,
|
||||
getLocalParticipant
|
||||
} from '../../../base/participants';
|
||||
import { Container } from '../../../base/react';
|
||||
import { connect } from '../../../base/redux';
|
||||
@@ -24,6 +25,8 @@ import { DisplayNameLabel } from '../../../display-name';
|
||||
import { toggleToolboxVisible } from '../../../toolbox/actions.native';
|
||||
import { RemoteVideoMenu } from '../../../video-menu';
|
||||
import ConnectionStatusComponent from '../../../video-menu/components/native/ConnectionStatusComponent';
|
||||
import SharedVideoMenu
|
||||
from '../../../video-menu/components/native/SharedVideoMenu';
|
||||
|
||||
import AudioMutedIndicator from './AudioMutedIndicator';
|
||||
import DominantSpeakerIndicator from './DominantSpeakerIndicator';
|
||||
@@ -48,6 +51,11 @@ type Props = {
|
||||
*/
|
||||
_largeVideo: Object,
|
||||
|
||||
/**
|
||||
* Shared video local participant owner.
|
||||
*/
|
||||
_localVideoOwner: boolean,
|
||||
|
||||
/**
|
||||
* The Redux representation of the participant to display.
|
||||
*/
|
||||
@@ -116,6 +124,7 @@ function Thumbnail(props: Props) {
|
||||
const {
|
||||
_audioMuted: audioMuted,
|
||||
_largeVideo: largeVideo,
|
||||
_localVideoOwner,
|
||||
_renderDominantSpeakerIndicator: renderDominantSpeakerIndicator,
|
||||
_renderModeratorIndicator: renderModeratorIndicator,
|
||||
_participant: participant,
|
||||
@@ -144,6 +153,12 @@ function Thumbnail(props: Props) {
|
||||
dispatch(openDialog(ConnectionStatusComponent, {
|
||||
participantID: participant.id
|
||||
}));
|
||||
} else if (participant.isFakeParticipant) {
|
||||
if (_localVideoOwner) {
|
||||
dispatch(openDialog(SharedVideoMenu, {
|
||||
participant
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
dispatch(openDialog(RemoteVideoMenu, {
|
||||
participant
|
||||
@@ -223,9 +238,11 @@ function _mapStateToProps(state, ownProps) {
|
||||
// filmstrip doesn't render the video of the participant who is rendered on
|
||||
// the stage i.e. as a large video.
|
||||
const largeVideo = state['features/large-video'];
|
||||
const { ownerId } = state['features/shared-video'];
|
||||
const tracks = state['features/base/tracks'];
|
||||
const { participantID } = ownProps;
|
||||
const participant = getParticipantByIdOrUndefined(state, participantID);
|
||||
const localParticipantId = getLocalParticipant(state).id;
|
||||
const id = participant?.id;
|
||||
const audioTrack
|
||||
= getTrackByMediaTypeAndParticipant(tracks, MEDIA_TYPE.AUDIO, id);
|
||||
@@ -240,6 +257,7 @@ function _mapStateToProps(state, ownProps) {
|
||||
return {
|
||||
_audioMuted: audioTrack?.muted ?? true,
|
||||
_largeVideo: largeVideo,
|
||||
_localVideoOwner: Boolean(ownerId === localParticipantId),
|
||||
_participant: participant,
|
||||
_renderDominantSpeakerIndicator: renderDominantSpeakerIndicator,
|
||||
_renderModeratorIndicator: renderModeratorIndicator,
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
sendAnalytics
|
||||
} from '../../../analytics';
|
||||
import { getToolbarButtons } from '../../../base/config';
|
||||
import { isMobileBrowser } from '../../../base/environment/utils';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { Icon, IconMenuDown, IconMenuUp } from '../../../base/icons';
|
||||
import { connect } from '../../../base/redux';
|
||||
@@ -17,7 +18,13 @@ import { showToolbox } from '../../../toolbox/actions.web';
|
||||
import { isButtonEnabled, isToolboxVisible } from '../../../toolbox/functions.web';
|
||||
import { LAYOUTS, getCurrentLayout } from '../../../video-layout';
|
||||
import { setFilmstripVisible, setVisibleRemoteParticipants } from '../../actions';
|
||||
import { TILE_HORIZONTAL_MARGIN, TILE_VERTICAL_MARGIN, TOOLBAR_HEIGHT } from '../../constants';
|
||||
import {
|
||||
ASPECT_RATIO_BREAKPOINT,
|
||||
TILE_HORIZONTAL_MARGIN,
|
||||
TILE_VERTICAL_MARGIN,
|
||||
TOOLBAR_HEIGHT,
|
||||
TOOLBAR_HEIGHT_MOBILE
|
||||
} from '../../constants';
|
||||
import { shouldRemoteVideosBeVisible } from '../../functions';
|
||||
|
||||
import AudioTracksContainer from './AudioTracksContainer';
|
||||
@@ -277,10 +284,10 @@ class Filmstrip extends PureComponent <Props> {
|
||||
* @param {Object} data - Information about the rendered items.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onListItemsRendered({ overscanStartIndex, overscanStopIndex }) {
|
||||
_onListItemsRendered({ visibleStartIndex, visibleStopIndex }) {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(setVisibleRemoteParticipants(overscanStartIndex, overscanStopIndex));
|
||||
dispatch(setVisibleRemoteParticipants(visibleStartIndex, visibleStopIndex));
|
||||
}
|
||||
|
||||
_onGridItemsRendered: Object => void;
|
||||
@@ -292,14 +299,14 @@ class Filmstrip extends PureComponent <Props> {
|
||||
* @returns {void}
|
||||
*/
|
||||
_onGridItemsRendered({
|
||||
overscanColumnStartIndex,
|
||||
overscanColumnStopIndex,
|
||||
overscanRowStartIndex,
|
||||
overscanRowStopIndex
|
||||
visibleColumnStartIndex,
|
||||
visibleColumnStopIndex,
|
||||
visibleRowStartIndex,
|
||||
visibleRowStopIndex
|
||||
}) {
|
||||
const { _columns, dispatch } = this.props;
|
||||
const startIndex = (overscanRowStartIndex * _columns) + overscanColumnStartIndex;
|
||||
const endIndex = (overscanRowStopIndex * _columns) + overscanColumnStopIndex;
|
||||
const startIndex = (visibleRowStartIndex * _columns) + visibleColumnStartIndex;
|
||||
const endIndex = (visibleRowStopIndex * _columns) + visibleColumnStopIndex;
|
||||
|
||||
dispatch(setVisibleRemoteParticipants(startIndex, endIndex));
|
||||
}
|
||||
@@ -338,6 +345,7 @@ class Filmstrip extends PureComponent <Props> {
|
||||
initialScrollTop = { 0 }
|
||||
itemKey = { this._gridItemKey }
|
||||
onItemsRendered = { this._onGridItemsRendered }
|
||||
overscanRowCount = { 1 }
|
||||
rowCount = { _rows }
|
||||
rowHeight = { _thumbnailHeight + TILE_VERTICAL_MARGIN }
|
||||
width = { _filmstripWidth }>
|
||||
@@ -356,6 +364,7 @@ class Filmstrip extends PureComponent <Props> {
|
||||
itemKey: this._listItemKey,
|
||||
itemSize: 0,
|
||||
onItemsRendered: this._onListItemsRendered,
|
||||
overscanCount: 1,
|
||||
width: _filmstripWidth,
|
||||
style: {
|
||||
willChange: 'auto'
|
||||
@@ -485,10 +494,6 @@ function _mapStateToProps(state) {
|
||||
const reduceHeight = state['features/toolbox'].visible && toolbarButtons.length;
|
||||
const remoteVideosVisible = shouldRemoteVideosBeVisible(state);
|
||||
const { isOpen: shiftRight } = state['features/chat'];
|
||||
const className = `${remoteVideosVisible ? '' : 'hide-videos'} ${
|
||||
reduceHeight ? 'reduce-height' : ''
|
||||
} ${shiftRight ? 'shift-right' : ''}`.trim();
|
||||
const videosClassName = `filmstrip__videos${visible ? '' : ' hidden'}`;
|
||||
const {
|
||||
gridDimensions = {},
|
||||
filmstripHeight,
|
||||
@@ -496,12 +501,35 @@ function _mapStateToProps(state) {
|
||||
thumbnailSize: tileViewThumbnailSize
|
||||
} = state['features/filmstrip'].tileViewDimensions;
|
||||
const _currentLayout = getCurrentLayout(state);
|
||||
|
||||
const { clientHeight, clientWidth } = state['features/base/responsive-ui'];
|
||||
const availableSpace = clientHeight - filmstripHeight;
|
||||
let filmstripPadding = 0;
|
||||
|
||||
if (availableSpace > 0) {
|
||||
const paddingValue = TOOLBAR_HEIGHT_MOBILE - availableSpace;
|
||||
|
||||
if (paddingValue > 0) {
|
||||
filmstripPadding = paddingValue;
|
||||
}
|
||||
} else {
|
||||
filmstripPadding = TOOLBAR_HEIGHT_MOBILE;
|
||||
}
|
||||
|
||||
const collapseTileView = reduceHeight
|
||||
&& isMobileBrowser()
|
||||
&& clientWidth <= ASPECT_RATIO_BREAKPOINT;
|
||||
|
||||
const className = `${remoteVideosVisible ? '' : 'hide-videos'} ${
|
||||
reduceHeight ? 'reduce-height' : ''
|
||||
} ${shiftRight ? 'shift-right' : ''} ${collapseTileView ? 'collapse' : ''}`.trim();
|
||||
const videosClassName = `filmstrip__videos${visible ? '' : ' hidden'}`;
|
||||
let _thumbnailSize, remoteFilmstripHeight, remoteFilmstripWidth;
|
||||
|
||||
switch (_currentLayout) {
|
||||
case LAYOUTS.TILE_VIEW:
|
||||
_thumbnailSize = tileViewThumbnailSize;
|
||||
remoteFilmstripHeight = filmstripHeight;
|
||||
remoteFilmstripHeight = filmstripHeight - (collapseTileView && filmstripPadding > 0 ? filmstripPadding : 0);
|
||||
remoteFilmstripWidth = filmstripWidth;
|
||||
break;
|
||||
case LAYOUTS.VERTICAL_FILMSTRIP_VIEW: {
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { isMobileBrowser } from '../../../../../react/features/base/environment/utils';
|
||||
import { createScreenSharingIssueEvent, sendAnalytics } from '../../../analytics';
|
||||
import { AudioLevelIndicator } from '../../../audio-level-indicator';
|
||||
import { Avatar } from '../../../base/avatar';
|
||||
import { isMobileBrowser } from '../../../base/environment/utils';
|
||||
import JitsiMeetJS from '../../../base/lib-jitsi-meet/_';
|
||||
import { MEDIA_TYPE, VideoTrack } from '../../../base/media';
|
||||
import {
|
||||
@@ -139,6 +139,11 @@ export type Props = {|
|
||||
*/
|
||||
_isCurrentlyOnLargeVideo: boolean,
|
||||
|
||||
/**
|
||||
* Whether we are currently running in a mobile browser.
|
||||
*/
|
||||
_isMobile: boolean,
|
||||
|
||||
/**
|
||||
* Indicates whether the participant is screen sharing.
|
||||
*/
|
||||
@@ -612,7 +617,7 @@ class Thumbnail extends Component<Props, State> {
|
||||
* @returns {ReactElement}
|
||||
*/
|
||||
_renderFakeParticipant() {
|
||||
const { _participant: { avatarURL } } = this.props;
|
||||
const { _isMobile, _participant: { avatarURL } } = this.props;
|
||||
const styles = this._getStyles();
|
||||
const containerClassName = this._getContainerClassName();
|
||||
|
||||
@@ -621,8 +626,10 @@ class Thumbnail extends Component<Props, State> {
|
||||
className = { containerClassName }
|
||||
id = 'sharedVideoContainer'
|
||||
onClick = { this._onClick }
|
||||
onMouseEnter = { this._onMouseEnter }
|
||||
onMouseLeave = { this._onMouseLeave }
|
||||
{ ...(_isMobile ? {} : {
|
||||
onMouseEnter: this._onMouseEnter,
|
||||
onMouseLeave: this._onMouseLeave
|
||||
}) }
|
||||
style = { styles.thumbnail }>
|
||||
{avatarURL ? (
|
||||
<img
|
||||
@@ -753,6 +760,7 @@ class Thumbnail extends Component<Props, State> {
|
||||
const {
|
||||
_defaultLocalDisplayName,
|
||||
_disableLocalVideoFlip,
|
||||
_isMobile,
|
||||
_isScreenSharing,
|
||||
_localFlipX,
|
||||
_disableProfile,
|
||||
@@ -772,13 +780,17 @@ class Thumbnail extends Component<Props, State> {
|
||||
className = { containerClassName }
|
||||
id = 'localVideoContainer'
|
||||
onClick = { this._onClick }
|
||||
onMouseEnter = { this._onMouseEnter }
|
||||
onMouseLeave = { this._onMouseLeave }
|
||||
{ ...(isMobileBrowser() ? {
|
||||
onTouchEnd: this._onTouchEnd,
|
||||
onTouchMove: this._onTouchMove,
|
||||
onTouchStart: this._onTouchStart
|
||||
} : {}) }
|
||||
{ ...(_isMobile
|
||||
? {
|
||||
onTouchEnd: this._onTouchEnd,
|
||||
onTouchMove: this._onTouchMove,
|
||||
onTouchStart: this._onTouchStart
|
||||
}
|
||||
: {
|
||||
onMouseEnter: this._onMouseEnter,
|
||||
onMouseLeave: this._onMouseLeave
|
||||
}
|
||||
) }
|
||||
style = { styles.thumbnail }>
|
||||
<div className = 'videocontainer__background' />
|
||||
<span id = 'localVideoWrapper'>
|
||||
@@ -875,6 +887,7 @@ class Thumbnail extends Component<Props, State> {
|
||||
*/
|
||||
_renderRemoteParticipant() {
|
||||
const {
|
||||
_isMobile,
|
||||
_isTestModeEnabled,
|
||||
_participant,
|
||||
_startSilent,
|
||||
@@ -909,13 +922,17 @@ class Thumbnail extends Component<Props, State> {
|
||||
className = { containerClassName }
|
||||
id = { `participant_${id}` }
|
||||
onClick = { this._onClick }
|
||||
onMouseEnter = { this._onMouseEnter }
|
||||
onMouseLeave = { this._onMouseLeave }
|
||||
{ ...(isMobileBrowser() ? {
|
||||
onTouchEnd: this._onTouchEnd,
|
||||
onTouchMove: this._onTouchMove,
|
||||
onTouchStart: this._onTouchStart
|
||||
} : {}) }
|
||||
{ ...(_isMobile
|
||||
? {
|
||||
onTouchEnd: this._onTouchEnd,
|
||||
onTouchMove: this._onTouchMove,
|
||||
onTouchStart: this._onTouchStart
|
||||
}
|
||||
: {
|
||||
onMouseEnter: this._onMouseEnter,
|
||||
onMouseLeave: this._onMouseLeave
|
||||
}
|
||||
) }
|
||||
style = { styles.thumbnail }>
|
||||
{
|
||||
_videoTrack && <VideoTrack
|
||||
@@ -1031,6 +1048,7 @@ function _mapStateToProps(state, ownProps): Object {
|
||||
} = state['features/base/config'];
|
||||
const { NORMAL = 8 } = interfaceConfig.INDICATOR_FONT_SIZES || {};
|
||||
const { localFlipX } = state['features/base/settings'];
|
||||
const _isMobile = isMobileBrowser();
|
||||
|
||||
|
||||
switch (_currentLayout) {
|
||||
@@ -1072,7 +1090,7 @@ function _mapStateToProps(state, ownProps): Object {
|
||||
return {
|
||||
_audioTrack,
|
||||
_connectionIndicatorAutoHideEnabled: interfaceConfig.CONNECTION_INDICATOR_AUTO_HIDE_ENABLED,
|
||||
_connectionIndicatorDisabled: isMobileBrowser() || interfaceConfig.CONNECTION_INDICATOR_DISABLED,
|
||||
_connectionIndicatorDisabled: _isMobile || interfaceConfig.CONNECTION_INDICATOR_DISABLED,
|
||||
_currentLayout,
|
||||
_defaultLocalDisplayName: interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME,
|
||||
_disableLocalVideoFlip: Boolean(disableLocalVideoFlip),
|
||||
@@ -1081,6 +1099,7 @@ function _mapStateToProps(state, ownProps): Object {
|
||||
_isAudioOnly: Boolean(state['features/base/audio-only'].enabled),
|
||||
_isCurrentlyOnLargeVideo: state['features/large-video']?.participantId === id,
|
||||
_isDominantSpeakerDisabled: interfaceConfig.DISABLE_DOMINANT_SPEAKER_INDICATOR,
|
||||
_isMobile,
|
||||
_isScreenSharing: _videoTrack?.videoType === 'desktop',
|
||||
_isTestModeEnabled: isTestModeEnabled(state),
|
||||
_isVideoPlayable: id && isVideoPlayable(state, id),
|
||||
|
||||
@@ -163,6 +163,11 @@ export const TILE_HORIZONTAL_MARGIN = 4;
|
||||
*/
|
||||
export const TOOLBAR_HEIGHT = 72;
|
||||
|
||||
/**
|
||||
* The height of the whole toolbar.
|
||||
*/
|
||||
export const TOOLBAR_HEIGHT_MOBILE = 60;
|
||||
|
||||
/**
|
||||
* The size of the horizontal border of a thumbnail.
|
||||
*
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @flow
|
||||
|
||||
import { getFeatureFlag, FILMSTRIP_ENABLED } from '../base/flags';
|
||||
import { getParticipantCountWithFake } from '../base/participants';
|
||||
import { getParticipantCountWithFake, getPinnedParticipant } from '../base/participants';
|
||||
import { toState } from '../base/redux';
|
||||
|
||||
/**
|
||||
@@ -25,3 +25,35 @@ export function isFilmstripVisible(stateful: Object | Function) {
|
||||
|
||||
return getParticipantCountWithFake(state) > 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the remote video thumbnails should be displayed/visible in
|
||||
* the filmstrip.
|
||||
*
|
||||
* @param {Object} state - The full redux state.
|
||||
* @returns {boolean} - If remote video thumbnails should be displayed/visible
|
||||
* in the filmstrip, then {@code true}; otherwise, {@code false}.
|
||||
*/
|
||||
export function shouldRemoteVideosBeVisible(state: Object) {
|
||||
if (state['features/invite'].calleeInfoVisible) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Include fake participants to derive how many thumbnails are dispalyed,
|
||||
// as it is assumed all participants, including fake, will be displayed
|
||||
// in the filmstrip.
|
||||
const participantCount = getParticipantCountWithFake(state);
|
||||
const pinnedParticipant = getPinnedParticipant(state);
|
||||
const { disable1On1Mode } = state['features/base/config'];
|
||||
|
||||
return Boolean(
|
||||
participantCount > 2
|
||||
|
||||
// Always show the filmstrip when there is another participant to
|
||||
// show and the local video is pinned. Note we are not taking the
|
||||
// toolbar visibility into account here (unlike web) because
|
||||
// showing / hiding views in quick succession on mobile is taxing.
|
||||
|| (participantCount > 1 && pinnedParticipant?.local)
|
||||
|
||||
|| disable1On1Mode);
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ const DEFAULT_STATE = {
|
||||
horizontalViewDimensions: {},
|
||||
|
||||
/**
|
||||
* The custom audio volume levels per perticipant.
|
||||
* The custom audio volume levels per participant.
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
|
||||
@@ -8,14 +8,15 @@ import { Dialog } from '../../../../base/dialog';
|
||||
import { translate } from '../../../../base/i18n';
|
||||
import { JitsiRecordingConstants } from '../../../../base/lib-jitsi-meet';
|
||||
import { connect } from '../../../../base/redux';
|
||||
import { isVpaasMeeting } from '../../../../billing-counter/functions';
|
||||
import { isDynamicBrandingDataLoaded } from '../../../../dynamic-branding/functions';
|
||||
import EmbedMeetingTrigger from '../../../../embed-meeting/components/EmbedMeetingTrigger';
|
||||
import { isVpaasMeeting } from '../../../../jaas/functions';
|
||||
import { getActiveSession } from '../../../../recording';
|
||||
import { updateDialInNumbers } from '../../../actions';
|
||||
import {
|
||||
_getDefaultPhoneNumber,
|
||||
getInviteText,
|
||||
getInviteTextiOS,
|
||||
isAddPeopleEnabled,
|
||||
isDialOutEnabled,
|
||||
sharingFeatures,
|
||||
@@ -62,6 +63,12 @@ type Props = {
|
||||
*/
|
||||
_invitationText: string,
|
||||
|
||||
/**
|
||||
* The custom no new-lines meeting invitation text for iOS default email.
|
||||
* Needed because of this mailto: iOS issue: https://developer.apple.com/forums/thread/681023
|
||||
*/
|
||||
_invitationTextiOS: string,
|
||||
|
||||
/**
|
||||
* An alternate app name to be displayed in the email subject.
|
||||
*/
|
||||
@@ -110,6 +117,7 @@ function AddPeopleDialog({
|
||||
_urlSharingVisible,
|
||||
_emailSharingVisible,
|
||||
_invitationText,
|
||||
_invitationTextiOS,
|
||||
_inviteAppName,
|
||||
_inviteContactsVisible,
|
||||
_inviteUrl,
|
||||
@@ -160,7 +168,8 @@ function AddPeopleDialog({
|
||||
_emailSharingVisible
|
||||
? <InviteByEmailSection
|
||||
inviteSubject = { inviteSubject }
|
||||
inviteText = { _invitationText } />
|
||||
inviteText = { _invitationText }
|
||||
inviteTextiOS = { _invitationTextiOS } />
|
||||
: null
|
||||
}
|
||||
{ _embedMeetingVisible && <EmbedMeetingTrigger /> }
|
||||
@@ -207,6 +216,9 @@ function mapStateToProps(state, ownProps) {
|
||||
_invitationText: getInviteText({ state,
|
||||
phoneNumber,
|
||||
t: ownProps.t }),
|
||||
_invitationTextiOS: getInviteTextiOS({ state,
|
||||
phoneNumber,
|
||||
t: ownProps.t }),
|
||||
_inviteAppName: inviteAppName,
|
||||
_inviteContactsVisible: interfaceConfig.ENABLE_DIAL_OUT && !hideInviteContacts,
|
||||
_inviteUrl: getInviteURL(state),
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { isIosMobileBrowser } from '../../../../base/environment/utils';
|
||||
import { translate } from '../../../../base/i18n';
|
||||
import {
|
||||
Icon,
|
||||
@@ -27,6 +28,11 @@ type Props = {
|
||||
*/
|
||||
inviteText: string,
|
||||
|
||||
/**
|
||||
* The encoded no new-lines iOS invitation text to be sent on default mail.
|
||||
*/
|
||||
inviteTextiOS: string,
|
||||
|
||||
/**
|
||||
* Invoked to obtain translated strings.
|
||||
*/
|
||||
@@ -38,10 +44,13 @@ type Props = {
|
||||
*
|
||||
* @returns {React$Element<any>}
|
||||
*/
|
||||
function InviteByEmailSection({ inviteSubject, inviteText, t }: Props) {
|
||||
function InviteByEmailSection({ inviteSubject, inviteText, inviteTextiOS, t }: Props) {
|
||||
const [ isActive, setIsActive ] = useState(false);
|
||||
const encodedInviteSubject = encodeURIComponent(inviteSubject);
|
||||
const encodedInviteText = encodeURIComponent(inviteText);
|
||||
const encodedInviteTextiOS = encodeURIComponent(inviteTextiOS);
|
||||
|
||||
const encodedDefaultEmailText = isIosMobileBrowser() ? encodedInviteTextiOS : encodedInviteText;
|
||||
|
||||
/**
|
||||
* Copies the conference invitation to the clipboard.
|
||||
@@ -100,7 +109,7 @@ function InviteByEmailSection({ inviteSubject, inviteText, t }: Props) {
|
||||
{
|
||||
icon: IconEmail,
|
||||
tooltipKey: 'addPeople.defaultEmail',
|
||||
url: `mailto:?subject=${encodedInviteSubject}&body=${encodedInviteText}`
|
||||
url: `mailto:?subject=${encodedInviteSubject}&body=${encodedDefaultEmailText}`
|
||||
},
|
||||
{
|
||||
icon: IconGoogle,
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Icon, IconPhone } from '../../../../base/icons';
|
||||
import { getLocalParticipant } from '../../../../base/participants';
|
||||
import { MultiSelectAutocomplete } from '../../../../base/react';
|
||||
import { connect } from '../../../../base/redux';
|
||||
import { isVpaasMeeting } from '../../../../billing-counter/functions';
|
||||
import { isVpaasMeeting } from '../../../../jaas/functions';
|
||||
import { hideAddPeopleDialog } from '../../../actions';
|
||||
import { INVITE_TYPES } from '../../../constants';
|
||||
import AbstractAddPeopleDialog, {
|
||||
@@ -511,11 +511,7 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
|
||||
const supportString = t('inlineDialogFailure.supportMsg');
|
||||
const supportLink = interfaceConfig.SUPPORT_URL;
|
||||
|
||||
if (!supportLink) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const supportLinkContent = (
|
||||
const supportLinkContent = supportLink ? (
|
||||
<span>
|
||||
<span>
|
||||
{ supportString.padEnd(supportString.length + 1) }
|
||||
@@ -524,13 +520,14 @@ class InviteContactsForm extends AbstractAddPeopleDialog<Props, State> {
|
||||
<a
|
||||
aria-label = { supportLink }
|
||||
href = { supportLink }
|
||||
rel = 'noopener noreferrer'>
|
||||
rel = 'noopener noreferrer'
|
||||
target = '_blank'>
|
||||
{ t('inlineDialogFailure.support') }
|
||||
</a>
|
||||
</span>
|
||||
<span>.</span>
|
||||
</span>
|
||||
);
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<div className = 'modal-dialog-form-error'>
|
||||
|
||||
@@ -58,7 +58,7 @@ class DialInSummary extends Component<Props> {
|
||||
headerLabelKey: 'info.label'
|
||||
}}
|
||||
modalId = { DIAL_IN_SUMMARY_VIEW_ID }
|
||||
style = { styles.backDrop } >
|
||||
style = { styles.backDrop }>
|
||||
<WebView
|
||||
onError = { this._onError }
|
||||
onShouldStartLoadWithRequest = { this._onNavigate }
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user