mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-09-22 21:19:17 +00:00
Compare commits
5 Commits
3456
...
base-sessi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d60a4e3a6 | ||
|
|
160380dac0 | ||
|
|
9dbba7b119 | ||
|
|
7101f90b6e | ||
|
|
621ee7b447 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -73,7 +73,6 @@ buck-out/
|
||||
# Build artifacts
|
||||
*.jsbundle
|
||||
*.framework
|
||||
android/app/debug
|
||||
android/app/release
|
||||
|
||||
# precommit-hook
|
||||
|
||||
@@ -28,7 +28,6 @@ import android.view.KeyEvent;
|
||||
import org.jitsi.meet.sdk.JitsiMeet;
|
||||
import org.jitsi.meet.sdk.JitsiMeetActivity;
|
||||
import org.jitsi.meet.sdk.JitsiMeetConferenceOptions;
|
||||
import org.jitsi.meet.sdk.JitsiMeetUserInfo;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
@@ -107,7 +106,7 @@ public class MainActivity extends JitsiMeetActivity {
|
||||
//
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (requestCode == OVERLAY_PERMISSION_REQUEST_CODE
|
||||
&& canRequestOverlayPermission()) {
|
||||
if (Settings.canDrawOverlays(this)) {
|
||||
|
||||
@@ -2,6 +2,5 @@
|
||||
<!-- Base application theme. -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<!-- Customize your theme here. -->
|
||||
<item name="android:navigationBarColor">#1081B2</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
@@ -169,7 +169,7 @@ ext {
|
||||
glideVersion = "4.7.1" // keep in sync with react-native-fast-image
|
||||
|
||||
// Libre build
|
||||
libreBuild = (System.env.LIBRE_BUILD ?: "false").toBoolean()
|
||||
libreBuild = (System.env.LIBRE_BUILD ?: "true").toBoolean()
|
||||
}
|
||||
|
||||
// If Android SDK is not installed, accept its license so that it
|
||||
|
||||
@@ -10,7 +10,6 @@ MVN_HTTP=0
|
||||
DEFAULT_SDK_VERSION=$(grep sdkVersion ${THIS_DIR}/../gradle.properties | cut -d"=" -f2)
|
||||
SDK_VERSION=${OVERRIDE_SDK_VERSION:-${DEFAULT_SDK_VERSION}}
|
||||
RN_VERSION=$(jq -r '.dependencies."react-native"' ${THIS_DIR}/../../package.json)
|
||||
DO_GIT_TAG=${GIT_TAG:-0}
|
||||
|
||||
if [[ $THE_MVN_REPO == http* ]]; then
|
||||
MVN_HTTP=1
|
||||
@@ -65,15 +64,17 @@ pushd ${THIS_DIR}/../
|
||||
./gradlew clean assembleRelease publish
|
||||
popd
|
||||
|
||||
if [[ $DO_GIT_TAG == 1 ]]; then
|
||||
if [[ $MVN_HTTP == 0 ]]; then
|
||||
# The artifacts are now on the Maven repo, commit them
|
||||
pushd ${MVN_REPO_PATH}
|
||||
git add -A .
|
||||
git commit -m "Jitsi Meet SDK + dependencies: ${SDK_VERSION}"
|
||||
if [[ "$(git rev-parse --is-inside-work-tree 2>/dev/null)" == "true" ]]; then
|
||||
git add -A .
|
||||
git commit -m "Jitsi Meet SDK + dependencies"
|
||||
fi
|
||||
popd
|
||||
|
||||
# Tag the release
|
||||
git tag android-sdk-${SDK_VERSION}
|
||||
git tag -a android-sdk-${SDK_VERSION}
|
||||
fi
|
||||
|
||||
# Done!
|
||||
|
||||
@@ -14,7 +14,6 @@ if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then
|
||||
exit 2
|
||||
fi
|
||||
else
|
||||
adb reverse tcp:8081 tcp:8081
|
||||
CMD="${THIS_DIR}/../../node_modules/react-native/scripts/launchPackager.command"
|
||||
if [[ `uname` == "Darwin" ]]; then
|
||||
open -g "${CMD}" || echo "Can't start packager automatically"
|
||||
|
||||
@@ -53,7 +53,6 @@ dependencies {
|
||||
|
||||
implementation project(':react-native-background-timer')
|
||||
implementation project(':react-native-calendar-events')
|
||||
implementation project(':react-native-community-async-storage')
|
||||
implementation(project(':react-native-fast-image')) {
|
||||
exclude group: 'com.android.support'
|
||||
}
|
||||
@@ -63,7 +62,6 @@ dependencies {
|
||||
implementation project(':react-native-sound')
|
||||
implementation project(':react-native-vector-icons')
|
||||
implementation project(':react-native-webrtc')
|
||||
implementation project(':react-native-webview')
|
||||
|
||||
testImplementation 'junit:junit:4.12'
|
||||
}
|
||||
@@ -207,7 +205,8 @@ publishing {
|
||||
def groupId = it.moduleGroup
|
||||
def artifactId = it.moduleName
|
||||
|
||||
if (artifactId.startsWith('react-native-') && groupId.equals('jitsi-meet')) {
|
||||
if (artifactId.startsWith('react-native-')
|
||||
&& groupId.equals('jitsi-meet')) {
|
||||
groupId = rootProject.ext.moduleGroupId
|
||||
}
|
||||
|
||||
|
||||
@@ -52,17 +52,6 @@ class AmplitudeModule
|
||||
Amplitude.getInstance(instanceName).initialize(getCurrentActivity(), apiKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user ID for an Amplitude instance.
|
||||
*
|
||||
* @param instanceName The name of the Amplitude instance.
|
||||
* @param userId The new value for the user ID.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void setUserId(String instanceName, String userId) {
|
||||
Amplitude.getInstance(instanceName).setUserId(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the user properties for an Amplitude instance.
|
||||
*
|
||||
|
||||
@@ -143,11 +143,6 @@ public class JitsiMeetActivity extends FragmentActivity
|
||||
// Activity lifecycle methods
|
||||
//
|
||||
|
||||
@Override
|
||||
public void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
JitsiMeetActivityDelegate.onActivityResult(this, requestCode, resultCode, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
JitsiMeetActivityDelegate.onBackPressed();
|
||||
|
||||
@@ -40,15 +40,6 @@ public class JitsiMeetActivityDelegate {
|
||||
private static PermissionListener permissionListener;
|
||||
private static Callback permissionsCallback;
|
||||
|
||||
/**
|
||||
* Tells whether or not the permissions request is currently in progress.
|
||||
*
|
||||
* @return {@code true} if the permssions are being requested or {@code false} otherwise.
|
||||
*/
|
||||
static boolean arePermissionsBeingRequested() {
|
||||
return permissionListener != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link Activity} lifecycle method which should be called from
|
||||
* {@code Activity#onActivityResult} so we are notified about results of external intents
|
||||
|
||||
@@ -40,10 +40,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
* Room name.
|
||||
*/
|
||||
private String room;
|
||||
/**
|
||||
* Conference subject.
|
||||
*/
|
||||
private String subject;
|
||||
/**
|
||||
* JWT token used for authentication.
|
||||
*/
|
||||
@@ -54,11 +50,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
*/
|
||||
private Bundle colorScheme;
|
||||
|
||||
/**
|
||||
* Feature flags. See: https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/flags/constants.js
|
||||
*/
|
||||
private Bundle featureFlags;
|
||||
|
||||
/**
|
||||
* Set to {@code true} to join the conference with audio / video muted or to start in audio
|
||||
* only mode respectively.
|
||||
@@ -68,9 +59,10 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
private Boolean videoMuted;
|
||||
|
||||
/**
|
||||
* USer information, to be used when no token is specified.
|
||||
* Set to {@code true} to enable the welcome page. Typically SDK users won't need this enabled
|
||||
* since the host application decides which meeting to join.
|
||||
*/
|
||||
private JitsiMeetUserInfo userInfo;
|
||||
private Boolean welcomePageEnabled;
|
||||
|
||||
/**
|
||||
* Class used to build the immutable {@link JitsiMeetConferenceOptions} object.
|
||||
@@ -78,20 +70,17 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
public static class Builder {
|
||||
private URL serverURL;
|
||||
private String room;
|
||||
private String subject;
|
||||
private String token;
|
||||
|
||||
private Bundle colorScheme;
|
||||
private Bundle featureFlags;
|
||||
|
||||
private Boolean audioMuted;
|
||||
private Boolean audioOnly;
|
||||
private Boolean videoMuted;
|
||||
|
||||
private JitsiMeetUserInfo userInfo;
|
||||
private Boolean welcomePageEnabled;
|
||||
|
||||
public Builder() {
|
||||
featureFlags = new Bundle();
|
||||
}
|
||||
|
||||
/**\
|
||||
@@ -116,17 +105,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the conference subject.
|
||||
* @param subject - Subject for the conference.
|
||||
* @return - The {@link Builder} object itself so the method calls can be chained.
|
||||
*/
|
||||
public Builder setSubject(String subject) {
|
||||
this.subject = subject;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the JWT token to be used for authentication when joining a conference.
|
||||
* @param token - The JWT token to be used for authentication.
|
||||
@@ -192,31 +170,7 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
* @return - The {@link Builder} object itself so the method calls can be chained.
|
||||
*/
|
||||
public Builder setWelcomePageEnabled(boolean enabled) {
|
||||
this.featureFlags.putBoolean("welcomepage.enabled", enabled);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFeatureFlag(String flag, boolean value) {
|
||||
this.featureFlags.putBoolean(flag, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFeatureFlag(String flag, String value) {
|
||||
this.featureFlags.putString(flag, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setFeatureFlag(String flag, int value) {
|
||||
this.featureFlags.putInt(flag, value);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder setUserInfo(JitsiMeetUserInfo userInfo) {
|
||||
this.userInfo = userInfo;
|
||||
this.welcomePageEnabled = enabled;
|
||||
|
||||
return this;
|
||||
}
|
||||
@@ -231,14 +185,12 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
|
||||
options.serverURL = this.serverURL;
|
||||
options.room = this.room;
|
||||
options.subject = this.subject;
|
||||
options.token = this.token;
|
||||
options.colorScheme = this.colorScheme;
|
||||
options.featureFlags = this.featureFlags;
|
||||
options.audioMuted = this.audioMuted;
|
||||
options.audioOnly = this.audioOnly;
|
||||
options.videoMuted = this.videoMuted;
|
||||
options.userInfo = this.userInfo;
|
||||
options.welcomePageEnabled = this.welcomePageEnabled;
|
||||
|
||||
return options;
|
||||
}
|
||||
@@ -249,33 +201,32 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
|
||||
private JitsiMeetConferenceOptions(Parcel in) {
|
||||
room = in.readString();
|
||||
subject = in.readString();
|
||||
token = in.readString();
|
||||
colorScheme = in.readBundle();
|
||||
featureFlags = in.readBundle();
|
||||
userInfo = new JitsiMeetUserInfo(in.readBundle());
|
||||
byte tmpAudioMuted = in.readByte();
|
||||
audioMuted = tmpAudioMuted == 0 ? null : tmpAudioMuted == 1;
|
||||
byte tmpAudioOnly = in.readByte();
|
||||
audioOnly = tmpAudioOnly == 0 ? null : tmpAudioOnly == 1;
|
||||
byte tmpVideoMuted = in.readByte();
|
||||
videoMuted = tmpVideoMuted == 0 ? null : tmpVideoMuted == 1;
|
||||
byte tmpWelcomePageEnabled = in.readByte();
|
||||
welcomePageEnabled = tmpWelcomePageEnabled == 0 ? null : tmpWelcomePageEnabled == 1;
|
||||
}
|
||||
|
||||
Bundle asProps() {
|
||||
Bundle props = new Bundle();
|
||||
|
||||
// Android always has the PiP flag set by default.
|
||||
if (!featureFlags.containsKey("pip.enabled")) {
|
||||
featureFlags.putBoolean("pip.enabled", true);
|
||||
}
|
||||
|
||||
props.putBundle("flags", featureFlags);
|
||||
|
||||
if (colorScheme != null) {
|
||||
props.putBundle("colorScheme", colorScheme);
|
||||
}
|
||||
|
||||
if (welcomePageEnabled != null) {
|
||||
props.putBoolean("welcomePageEnabled", welcomePageEnabled);
|
||||
}
|
||||
|
||||
// TODO: get rid of this.
|
||||
props.putBoolean("pictureInPictureEnabled", true);
|
||||
|
||||
Bundle config = new Bundle();
|
||||
|
||||
if (audioMuted != null) {
|
||||
@@ -287,9 +238,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
if (videoMuted != null) {
|
||||
config.putBoolean("startWithVideoMuted", videoMuted);
|
||||
}
|
||||
if (subject != null) {
|
||||
config.putString("subject", subject);
|
||||
}
|
||||
|
||||
Bundle urlProps = new Bundle();
|
||||
|
||||
@@ -309,10 +257,6 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
urlProps.putString("jwt", token);
|
||||
}
|
||||
|
||||
if (token == null && userInfo != null) {
|
||||
props.putBundle("userInfo", userInfo.asBundle());
|
||||
}
|
||||
|
||||
urlProps.putBundle("config", config);
|
||||
props.putBundle("url", urlProps);
|
||||
|
||||
@@ -337,14 +281,12 @@ public class JitsiMeetConferenceOptions implements Parcelable {
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeString(room);
|
||||
dest.writeString(subject);
|
||||
dest.writeString(token);
|
||||
dest.writeBundle(colorScheme);
|
||||
dest.writeBundle(featureFlags);
|
||||
dest.writeBundle(userInfo.asBundle());
|
||||
dest.writeByte((byte) (audioMuted == null ? 0 : audioMuted ? 1 : 2));
|
||||
dest.writeByte((byte) (audioOnly == null ? 0 : audioOnly ? 1 : 2));
|
||||
dest.writeByte((byte) (videoMuted == null ? 0 : videoMuted ? 1 : 2));
|
||||
dest.writeByte((byte) (welcomePageEnabled == null ? 0 : welcomePageEnabled ? 1 : 2));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -26,6 +26,8 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* Base {@link Fragment} for applications integrating Jitsi Meet at a higher level. It
|
||||
* contains all the required wiring between the {@code JitsiMeetView} and
|
||||
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.os.Bundle;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
/**
|
||||
* This class represents user information to be passed to {@link JitsiMeetConferenceOptions} for
|
||||
* identifying a user.
|
||||
*/
|
||||
public class JitsiMeetUserInfo {
|
||||
/**
|
||||
* User's display name.
|
||||
*/
|
||||
private String displayName;
|
||||
|
||||
/**
|
||||
* User's email address.
|
||||
*/
|
||||
private String email;
|
||||
|
||||
/**
|
||||
* User's avatar URL.
|
||||
*/
|
||||
private URL avatar;
|
||||
|
||||
public JitsiMeetUserInfo() {}
|
||||
|
||||
public JitsiMeetUserInfo(Bundle b) {
|
||||
super();
|
||||
|
||||
if (b.containsKey("displayName")) {
|
||||
displayName = b.getString("displayName");
|
||||
}
|
||||
|
||||
if (b.containsKey("email")) {
|
||||
email = b.getString("email");
|
||||
}
|
||||
|
||||
if (b.containsKey("avatarURL")) {
|
||||
String avatarURL = b.getString("avatarURL");
|
||||
try {
|
||||
avatar = new URL(avatarURL);
|
||||
} catch (MalformedURLException e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
public void setDisplayName(String displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public URL getAvatar() {
|
||||
return avatar;
|
||||
}
|
||||
|
||||
public void setAvatar(URL avatar) {
|
||||
this.avatar = avatar;
|
||||
}
|
||||
|
||||
Bundle asBundle() {
|
||||
Bundle b = new Bundle();
|
||||
|
||||
if (displayName != null) {
|
||||
b.putString("displayName", displayName);
|
||||
}
|
||||
|
||||
if (email != null) {
|
||||
b.putString("email", email);
|
||||
}
|
||||
|
||||
if (avatar != null) {
|
||||
b.putString("avatarURL", avatar.toString());
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
}
|
||||
@@ -123,7 +123,6 @@ public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener> {
|
||||
PictureInPictureModule.class);
|
||||
if (pipModule != null
|
||||
&& PictureInPictureModule.isPictureInPictureSupported()
|
||||
&& !JitsiMeetActivityDelegate.arePermissionsBeingRequested()
|
||||
&& this.url != null) {
|
||||
try {
|
||||
pipModule.enterPictureInPicture();
|
||||
|
||||
@@ -66,7 +66,7 @@ class ReactInstanceManagerHolder {
|
||||
}
|
||||
|
||||
try {
|
||||
Class<?> amplitudeModuleClass = Class.forName("org.jitsi.meet.sdk.AmplitudeModule");
|
||||
Class<?> amplitudeModuleClass = Class.forName("AmplitudeModule");
|
||||
Constructor constructor = amplitudeModuleClass.getConstructor(ReactApplicationContext.class);
|
||||
nativeModules.add((NativeModule)constructor.newInstance(reactContext));
|
||||
} catch (Exception e) {
|
||||
@@ -147,8 +147,6 @@ class ReactInstanceManagerHolder {
|
||||
new com.oblador.vectoricons.VectorIconsPackage(),
|
||||
new com.ocetnik.timer.BackgroundTimerPackage(),
|
||||
new com.oney.WebRTCModule.WebRTCModulePackage(),
|
||||
new com.reactnativecommunity.asyncstorage.AsyncStoragePackage(),
|
||||
new com.reactnativecommunity.webview.RNCWebViewPackage(),
|
||||
new com.rnimmersive.RNImmersivePackage(),
|
||||
new com.zmxv.RNSound.RNSoundPackage(),
|
||||
new ReactPackageAdapter() {
|
||||
|
||||
@@ -3,10 +3,6 @@ rootProject.name = 'jitsi-meet'
|
||||
include ':app', ':sdk'
|
||||
include ':react-native-background-timer'
|
||||
project(':react-native-background-timer').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-background-timer/android')
|
||||
include ':react-native-calendar-events'
|
||||
project(':react-native-calendar-events').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-calendar-events/android')
|
||||
include ':react-native-community-async-storage'
|
||||
project(':react-native-community-async-storage').projectDir = new File(rootProject.projectDir, '../node_modules/@react-native-community/async-storage/android')
|
||||
include ':react-native-fast-image'
|
||||
project(':react-native-fast-image').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-fast-image/android')
|
||||
include ':react-native-google-signin'
|
||||
@@ -23,5 +19,5 @@ include ':react-native-vector-icons'
|
||||
project(':react-native-vector-icons').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-vector-icons/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')
|
||||
include ':react-native-calendar-events'
|
||||
project(':react-native-calendar-events').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-calendar-events/android')
|
||||
|
||||
118
conference.js
118
conference.js
@@ -16,7 +16,6 @@ import * as JitsiMeetConferenceEvents from './ConferenceEvents';
|
||||
|
||||
import {
|
||||
createDeviceChangedEvent,
|
||||
createStartSilentEvent,
|
||||
createScreenSharingEvent,
|
||||
createStreamSwitchDelayEvent,
|
||||
createTrackMutedEvent,
|
||||
@@ -52,8 +51,6 @@ import {
|
||||
import {
|
||||
checkAndNotifyForNewDevice,
|
||||
getAvailableDevices,
|
||||
notifyCameraError,
|
||||
notifyMicError,
|
||||
setAudioOutputDeviceId,
|
||||
updateDeviceList
|
||||
} from './react/features/base/devices';
|
||||
@@ -86,8 +83,6 @@ import {
|
||||
localParticipantConnectionStatusChanged,
|
||||
localParticipantRoleChanged,
|
||||
participantConnectionStatusChanged,
|
||||
participantKicked,
|
||||
participantMutedUs,
|
||||
participantPresenceChanged,
|
||||
participantRoleChanged,
|
||||
participantUpdated
|
||||
@@ -105,7 +100,6 @@ import {
|
||||
getLocationContextRoot,
|
||||
getJitsiMeetGlobalNS
|
||||
} from './react/features/base/util';
|
||||
import { notifyKickedOut } from './react/features/conference';
|
||||
import { addMessage } from './react/features/chat';
|
||||
import { showDesktopPicker } from './react/features/desktop-picker';
|
||||
import { appendSuffix } from './react/features/display-name';
|
||||
@@ -379,6 +373,13 @@ class ConferenceConnector {
|
||||
APP.UI.notifyGracefulShutdown();
|
||||
break;
|
||||
|
||||
case JitsiConferenceErrors.JINGLE_FATAL_ERROR: {
|
||||
const [ error ] = params;
|
||||
|
||||
APP.UI.notifyInternalError(error);
|
||||
break;
|
||||
}
|
||||
|
||||
case JitsiConferenceErrors.CONFERENCE_DESTROYED: {
|
||||
const [ reason ] = params;
|
||||
|
||||
@@ -400,7 +401,6 @@ class ConferenceConnector {
|
||||
|
||||
case JitsiConferenceErrors.FOCUS_LEFT:
|
||||
case JitsiConferenceErrors.VIDEOBRIDGE_NOT_AVAILABLE:
|
||||
case JitsiConferenceErrors.OFFER_ANSWER_FAILED:
|
||||
APP.store.dispatch(conferenceWillLeave(room));
|
||||
|
||||
// FIXME the conference should be stopped by the library and not by
|
||||
@@ -486,13 +486,10 @@ class ConferenceConnector {
|
||||
* call in hangup() to resolve when all operations are finished.
|
||||
*/
|
||||
function disconnect() {
|
||||
const onDisconnected = () => {
|
||||
APP.API.notifyConferenceLeft(APP.conference.roomName);
|
||||
connection.disconnect();
|
||||
APP.API.notifyConferenceLeft(APP.conference.roomName);
|
||||
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
return connection.disconnect().then(onDisconnected, onDisconnected);
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -697,14 +694,13 @@ export default {
|
||||
// If both requests for 'audio' + 'video' and 'audio'
|
||||
// only failed, we assume that there are some problems
|
||||
// with user's microphone and show corresponding dialog.
|
||||
APP.store.dispatch(notifyMicError(audioOnlyError));
|
||||
APP.store.dispatch(notifyCameraError(videoOnlyError));
|
||||
APP.UI.showMicErrorNotification(audioOnlyError);
|
||||
APP.UI.showCameraErrorNotification(videoOnlyError);
|
||||
} else {
|
||||
// If request for 'audio' + 'video' failed, but request
|
||||
// for 'audio' only was OK, we assume that we had
|
||||
// problems with camera and show corresponding dialog.
|
||||
APP.store.dispatch(
|
||||
notifyCameraError(audioAndVideoError));
|
||||
APP.UI.showCameraErrorNotification(audioAndVideoError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -722,22 +718,13 @@ export default {
|
||||
this.roomName = options.roomName;
|
||||
|
||||
return (
|
||||
|
||||
// Initialize the device list first. This way, when creating tracks
|
||||
// based on preferred devices, loose label matching can be done in
|
||||
// cases where the exact ID match is no longer available, such as
|
||||
// when the camera device has switched USB ports.
|
||||
// when in startSilent mode we want to start with audio muted
|
||||
this._initDeviceList()
|
||||
.catch(error => logger.warn(
|
||||
'initial device list initialization failed', error))
|
||||
.then(() => this.createInitialLocalTracksAndConnect(
|
||||
this.createInitialLocalTracksAndConnect(
|
||||
options.roomName, {
|
||||
startAudioOnly: config.startAudioOnly,
|
||||
startScreenSharing: config.startScreenSharing,
|
||||
startWithAudioMuted: config.startWithAudioMuted || config.startSilent,
|
||||
startWithAudioMuted: config.startWithAudioMuted,
|
||||
startWithVideoMuted: config.startWithVideoMuted
|
||||
}))
|
||||
})
|
||||
.then(([ tracks, con ]) => {
|
||||
tracks.forEach(track => {
|
||||
if ((track.isAudioTrack() && this.isLocalAudioMuted())
|
||||
@@ -782,23 +769,12 @@ export default {
|
||||
this.setVideoMuteStatus(true);
|
||||
}
|
||||
|
||||
// Initialize device list a second time to ensure device labels
|
||||
// get populated in case of an initial gUM acceptance; otherwise
|
||||
// they may remain as empty strings.
|
||||
this._initDeviceList(true);
|
||||
this._initDeviceList();
|
||||
|
||||
if (config.iAmRecorder) {
|
||||
this.recorder = new Recorder();
|
||||
}
|
||||
|
||||
if (config.startSilent) {
|
||||
sendAnalytics(createStartSilentEvent());
|
||||
APP.store.dispatch(showNotification({
|
||||
descriptionKey: 'notify.startSilentDescription',
|
||||
titleKey: 'notify.startSilentTitle'
|
||||
}));
|
||||
}
|
||||
|
||||
// XXX The API will take care of disconnecting from the XMPP
|
||||
// server (and, thus, leaving the room) on unload.
|
||||
return new Promise((resolve, reject) => {
|
||||
@@ -852,7 +828,7 @@ export default {
|
||||
|
||||
if (!this.localAudio && !mute) {
|
||||
const maybeShowErrorDialog = error => {
|
||||
showUI && APP.store.dispatch(notifyMicError(error));
|
||||
showUI && APP.UI.showMicErrorNotification(error);
|
||||
};
|
||||
|
||||
createLocalTracksF({ devices: [ 'audio' ] }, false)
|
||||
@@ -915,7 +891,7 @@ export default {
|
||||
|
||||
if (!this.localVideo && !mute) {
|
||||
const maybeShowErrorDialog = error => {
|
||||
showUI && APP.store.dispatch(notifyCameraError(error));
|
||||
showUI && APP.UI.showCameraErrorNotification(error);
|
||||
};
|
||||
|
||||
// Try to create local video if there wasn't any.
|
||||
@@ -1855,12 +1831,6 @@ export default {
|
||||
APP.UI.setAudioLevel(id, newLvl);
|
||||
});
|
||||
|
||||
room.on(JitsiConferenceEvents.TRACK_MUTE_CHANGED, (_, participantThatMutedUs) => {
|
||||
if (participantThatMutedUs) {
|
||||
APP.store.dispatch(participantMutedUs(participantThatMutedUs));
|
||||
}
|
||||
});
|
||||
|
||||
room.on(JitsiConferenceEvents.TALK_WHILE_MUTED, () => {
|
||||
APP.UI.showToolbar(6000);
|
||||
});
|
||||
@@ -1961,17 +1931,13 @@ export default {
|
||||
}
|
||||
});
|
||||
|
||||
room.on(JitsiConferenceEvents.KICKED, participant => {
|
||||
room.on(JitsiConferenceEvents.KICKED, () => {
|
||||
APP.UI.hideStats();
|
||||
APP.store.dispatch(notifyKickedOut(participant));
|
||||
APP.UI.notifyKicked();
|
||||
|
||||
// FIXME close
|
||||
});
|
||||
|
||||
room.on(JitsiConferenceEvents.PARTICIPANT_KICKED, (kicker, kicked) => {
|
||||
APP.store.dispatch(participantKicked(kicker, kicked));
|
||||
});
|
||||
|
||||
room.on(JitsiConferenceEvents.SUSPEND_DETECTED, () => {
|
||||
APP.store.dispatch(suspendDetected());
|
||||
|
||||
@@ -2132,7 +2098,7 @@ export default {
|
||||
this._updateVideoDeviceId();
|
||||
})
|
||||
.catch(err => {
|
||||
APP.store.dispatch(notifyCameraError(err));
|
||||
APP.UI.showCameraErrorNotification(err);
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -2165,7 +2131,7 @@ export default {
|
||||
this._updateAudioDeviceId();
|
||||
})
|
||||
.catch(err => {
|
||||
APP.store.dispatch(notifyMicError(err));
|
||||
APP.UI.showMicErrorNotification(err);
|
||||
});
|
||||
}
|
||||
);
|
||||
@@ -2267,8 +2233,7 @@ export default {
|
||||
APP.keyboardshortcut.init();
|
||||
|
||||
if (config.requireDisplayName
|
||||
&& !APP.conference.getLocalDisplayName()
|
||||
&& !this._room.isHidden()) {
|
||||
&& !APP.conference.getLocalDisplayName()) {
|
||||
APP.UI.promptDisplayName();
|
||||
}
|
||||
|
||||
@@ -2312,23 +2277,20 @@ export default {
|
||||
},
|
||||
|
||||
/**
|
||||
* Updates the list of current devices.
|
||||
* @param {boolean} setDeviceListChangeHandler - Whether to add the deviceList change handlers.
|
||||
* Inits list of current devices and event listener for device change.
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
_initDeviceList(setDeviceListChangeHandler = false) {
|
||||
_initDeviceList() {
|
||||
const { mediaDevices } = JitsiMeetJS;
|
||||
|
||||
if (mediaDevices.isDeviceListAvailable()
|
||||
&& mediaDevices.isDeviceChangeAvailable()) {
|
||||
if (setDeviceListChangeHandler) {
|
||||
this.deviceChangeListener = devices =>
|
||||
window.setTimeout(() => this._onDeviceListChanged(devices), 0);
|
||||
mediaDevices.addEventListener(
|
||||
JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,
|
||||
this.deviceChangeListener);
|
||||
}
|
||||
this.deviceChangeListener = devices =>
|
||||
window.setTimeout(() => this._onDeviceListChanged(devices), 0);
|
||||
mediaDevices.addEventListener(
|
||||
JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED,
|
||||
this.deviceChangeListener);
|
||||
|
||||
const { dispatch } = APP.store;
|
||||
|
||||
@@ -2426,26 +2388,17 @@ export default {
|
||||
// Let's handle unknown/non-preferred devices
|
||||
const newAvailDevices
|
||||
= APP.store.getState()['features/base/devices'].availableDevices;
|
||||
let newAudioDevices = [];
|
||||
let oldAudioDevices = [];
|
||||
|
||||
if (typeof newDevices.audiooutput === 'undefined') {
|
||||
newAudioDevices = newAvailDevices.audioOutput;
|
||||
oldAudioDevices = oldDevices.audioOutput;
|
||||
APP.store.dispatch(
|
||||
checkAndNotifyForNewDevice(newAvailDevices.audioOutput, oldDevices.audioOutput));
|
||||
}
|
||||
|
||||
if (!requestedInput.audio) {
|
||||
newAudioDevices = newAudioDevices.concat(newAvailDevices.audioInput);
|
||||
oldAudioDevices = oldAudioDevices.concat(oldDevices.audioInput);
|
||||
}
|
||||
|
||||
// check for audio
|
||||
if (newAudioDevices.length > 0) {
|
||||
APP.store.dispatch(
|
||||
checkAndNotifyForNewDevice(newAudioDevices, oldAudioDevices));
|
||||
checkAndNotifyForNewDevice(newAvailDevices.audioInput, oldDevices.audioInput));
|
||||
}
|
||||
|
||||
// check for video
|
||||
if (!requestedInput.video) {
|
||||
APP.store.dispatch(
|
||||
checkAndNotifyForNewDevice(newAvailDevices.videoInput, oldDevices.videoInput));
|
||||
@@ -2624,7 +2577,8 @@ export default {
|
||||
leaveRoomAndDisconnect() {
|
||||
APP.store.dispatch(conferenceWillLeave(room));
|
||||
|
||||
return room.leave().then(disconnect, disconnect);
|
||||
return room.leave()
|
||||
.then(disconnect, disconnect);
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
16
config.js
16
config.js
@@ -90,10 +90,6 @@ var config = {
|
||||
// applied locally. FIXME: having these 2 options is confusing.
|
||||
// startWithAudioMuted: false,
|
||||
|
||||
// Enabling it (with #params) will disable local audio output of remote
|
||||
// participants and to enable it back a reload is needed.
|
||||
// startSilent: false
|
||||
|
||||
// Video
|
||||
|
||||
// Sets the preferred resolution (height) for local video. Defaults to 720.
|
||||
@@ -270,13 +266,6 @@ var config = {
|
||||
// Whether or not some features are checked based on token.
|
||||
// enableFeaturesBasedOnToken: false,
|
||||
|
||||
// Enable lock room for all moderators, even when userRolesBasedOnToken is enabled and participants are guests.
|
||||
// lockRoomGuestEnabled: false,
|
||||
|
||||
// When enabled the password used for locking a room is restricted to up to the number of digits specified
|
||||
// roomPasswordNumberOfDigits: 10,
|
||||
// default: roomPasswordNumberOfDigits: false,
|
||||
|
||||
// Message to show the users. Example: 'The service will be down for
|
||||
// maintenance at 01:00 AM GMT,
|
||||
// noticeMessage: '',
|
||||
@@ -418,10 +407,6 @@ var config = {
|
||||
// use only.
|
||||
// _desktopSharingSourceDevice: 'sample-id-or-label'
|
||||
|
||||
// A property to disable the right click context menu for localVideo
|
||||
// the menu has option to flip the locally seen video for local presentations
|
||||
// disableLocalVideoFlip: false
|
||||
|
||||
// List of undocumented settings used in jitsi-meet
|
||||
/**
|
||||
_immediateReloadThreshold
|
||||
@@ -436,6 +421,7 @@ var config = {
|
||||
dialOutCodesUrl
|
||||
disableRemoteControl
|
||||
displayJids
|
||||
enableLocalVideoFlip
|
||||
etherpad_base
|
||||
externalConnectUrl
|
||||
firefox_fake_device
|
||||
|
||||
@@ -5,7 +5,8 @@ import jitsiLocalStorage from './modules/util/JitsiLocalStorage';
|
||||
|
||||
import {
|
||||
connectionEstablished,
|
||||
connectionFailed
|
||||
connectionFailed,
|
||||
connectionWillConnect
|
||||
} from './react/features/base/connection';
|
||||
import {
|
||||
isFatalJitsiConnectionError,
|
||||
@@ -74,6 +75,7 @@ function checkForAttachParametersAndConnect(id, password, connection) {
|
||||
function connect(id, password, roomName) {
|
||||
const connectionConfig = Object.assign({}, config);
|
||||
const { issuer, jwt } = APP.store.getState()['features/base/jwt'];
|
||||
const { locationURL } = APP.store.getState()['features/base/connection'];
|
||||
|
||||
connectionConfig.bosh += `?room=${roomName}`;
|
||||
|
||||
@@ -83,6 +85,8 @@ function connect(id, password, roomName) {
|
||||
jwt && issuer && issuer !== 'anonymous' ? jwt : undefined,
|
||||
connectionConfig);
|
||||
|
||||
APP.store.dispatch(connectionWillConnect(connection, locationURL));
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
connection.addEventListener(
|
||||
JitsiConnectionEvents.CONNECTION_ESTABLISHED,
|
||||
|
||||
194
css/_chat.scss
194
css/_chat.scss
@@ -1,9 +1,6 @@
|
||||
#sideToolbarContainer {
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
box-sizing: border-box;
|
||||
color: #FFF;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/**
|
||||
* Make the sidebar flush with the top of the toolbar. Take the size of
|
||||
* the toolbar and subtract from 100%.
|
||||
@@ -24,6 +21,20 @@
|
||||
&.slideInExt {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.sideToolbarContainer__inner {
|
||||
box-sizing: border-box;
|
||||
color: #FFF;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: $sidebarWidth;
|
||||
}
|
||||
}
|
||||
|
||||
#chat_container * {
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
#chatconversation {
|
||||
@@ -31,8 +42,9 @@
|
||||
flex: 1;
|
||||
font-size: 10pt;
|
||||
line-height: 20px;
|
||||
margin-top: $desktopAppDragBarHeight + 5px;
|
||||
overflow: auto;
|
||||
padding: 16px;
|
||||
padding: 5px;
|
||||
text-align: left;
|
||||
width: $sidebarWidth;
|
||||
word-wrap: break-word;
|
||||
@@ -80,41 +92,26 @@
|
||||
}
|
||||
}
|
||||
|
||||
.chat-header {
|
||||
background-color: $chatHeaderBackgroundColor;
|
||||
height: 70px;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
.chat-close {
|
||||
background: gray;
|
||||
border: 3px solid rgba(255, 255, 255, 0.1);
|
||||
border-radius: 100%;
|
||||
color: white;
|
||||
cursor:pointer;
|
||||
height: 10px;
|
||||
line-height: 10px;
|
||||
padding: 4px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
text-align: center;
|
||||
top: $desktopAppDragBarHeight;
|
||||
width: 10px;
|
||||
z-index: 1;
|
||||
|
||||
.chat-close {
|
||||
align-items: center;
|
||||
bottom: 8px;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
font-size: 18px;
|
||||
height: 40px;
|
||||
justify-content: center;
|
||||
line-height: 15px;
|
||||
padding: 4px;
|
||||
position: absolute;
|
||||
right: 5px;
|
||||
width: 40px;
|
||||
|
||||
&:hover {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#chat-input {
|
||||
border-top: 1px solid $chatInputSeparatorColor;
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
display: flex;
|
||||
|
||||
* {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
.remoteuser {
|
||||
@@ -126,13 +123,16 @@
|
||||
}
|
||||
|
||||
#usermsg {
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
border: 0px none;
|
||||
border-radius:0;
|
||||
box-shadow: none;
|
||||
color: white;
|
||||
font-size: 15px;
|
||||
font-size: 10pt;
|
||||
line-height: 30px;
|
||||
padding: 5px;
|
||||
max-height:150px;
|
||||
min-height:35px;
|
||||
overflow-y: auto;
|
||||
resize: none;
|
||||
width: 100%;
|
||||
@@ -145,47 +145,64 @@
|
||||
}
|
||||
|
||||
#nickname {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
color: #9d9d9d;
|
||||
font-size: 18px;
|
||||
margin-top: 30px;
|
||||
top: 100px;
|
||||
left: 5px;
|
||||
right: 5px;
|
||||
width: 95%;
|
||||
}
|
||||
|
||||
.sideToolbarContainer {
|
||||
* {
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
#chat_container .display-name {
|
||||
float: left;
|
||||
padding-left: 5px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
width: 95%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.display-name {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
#chat_container .timestamp {
|
||||
float: right;
|
||||
padding-right: 5px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.usermessage {
|
||||
padding-top: 20px;
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
.chatArrow {
|
||||
border-color:
|
||||
transparent $newToolbarBackgroundColor transparent transparent;
|
||||
border-style: solid;
|
||||
border-width: 0 10px 10px 0;
|
||||
left: -10px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.chatmessage {
|
||||
background-color: $chatRemoteMessageBackgroundColor;
|
||||
border-radius: 0px 6px 6px 6px;
|
||||
box-sizing: border-box;
|
||||
color: white;
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
width: 93%;
|
||||
margin-left: 9px;
|
||||
margin-right: auto;
|
||||
border-radius: 5px;
|
||||
border-top-left-radius: 0px;
|
||||
margin-top: 3px;
|
||||
max-width: 100%;
|
||||
color: white;
|
||||
padding-bottom: 3px;
|
||||
position: relative;
|
||||
|
||||
&.localuser {
|
||||
background-color: $chatLocalMessageBackgroundColor;
|
||||
border-radius: 6px 0px 6px 6px;
|
||||
&.localuser .display-name {
|
||||
color: #4C9AFF
|
||||
}
|
||||
|
||||
&.error {
|
||||
border-radius: 0px;
|
||||
|
||||
.chatArrow,
|
||||
.timestamp,
|
||||
.display-name {
|
||||
display: none;
|
||||
@@ -214,6 +231,7 @@
|
||||
}
|
||||
|
||||
#smileysarea {
|
||||
background-color: $newToolbarBackgroundColor;
|
||||
display: flex;
|
||||
max-height: 150px;
|
||||
min-height: 35px;
|
||||
@@ -228,22 +246,14 @@
|
||||
.smileys-panel {
|
||||
bottom: 100%;
|
||||
box-sizing: border-box;
|
||||
height: auto;
|
||||
max-height: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
transition: height 0.3s;
|
||||
width: $sidebarWidth;
|
||||
|
||||
/**
|
||||
* CSS transitions do not apply for auto dimensions. So to produce the css
|
||||
* accordion effect for showing and hiding the smiley-panel, while allowing
|
||||
* for variable panel, height, use a very large max-height and animate off
|
||||
* of that.
|
||||
*/
|
||||
transition: max-height 0.3s;
|
||||
|
||||
&.show-smileys {
|
||||
max-height: 500%;
|
||||
height: 146px;
|
||||
}
|
||||
|
||||
#smileysContainer {
|
||||
@@ -277,49 +287,3 @@
|
||||
#usermsg::-webkit-scrollbar-track-piece {
|
||||
background: #3a3a3a;
|
||||
}
|
||||
|
||||
.chat-message-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
&.local {
|
||||
align-items: flex-end;
|
||||
|
||||
.chatmessage {
|
||||
background-color: $chatLocalMessageBackgroundColor;
|
||||
border-radius: 6px 0px 6px 6px;
|
||||
}
|
||||
|
||||
.display-name {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.timestamp {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
&.error {
|
||||
.chatmessage {
|
||||
border-radius: 0px;
|
||||
color: red;
|
||||
}
|
||||
|
||||
.display-name {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.chatmessage-wrapper {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.chatmessage {
|
||||
background-color: $chatRemoteMessageBackgroundColor;
|
||||
border-radius: 0px 6px 6px 6px;
|
||||
display: inline-block;
|
||||
margin-top: 3px;
|
||||
color: white;
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,9 +116,9 @@
|
||||
}
|
||||
}
|
||||
i.disabled, .disabled i {
|
||||
cursor: initial !important;
|
||||
color: #fff !important;
|
||||
background-color: #a4b8d1 !important;
|
||||
cursor: initial;
|
||||
color: #fff;
|
||||
background-color: #a4b8d1;
|
||||
}
|
||||
|
||||
.icon-mic-disabled, .icon-microphone, .icon-camera-disabled, .icon-camera {
|
||||
|
||||
@@ -83,19 +83,11 @@ $modalMockAKInputBorder: 1px solid #f4f5f7;
|
||||
$modalTextColor: #333;
|
||||
|
||||
/**
|
||||
* Chat
|
||||
*/
|
||||
$chatHeaderBackgroundColor: rgba(42, 58, 75, 0.9);
|
||||
$chatInputSeparatorColor: #A4B8D1;
|
||||
$chatLocalMessageBackgroundColor: rgba(26, 108, 180, 1);
|
||||
$chatRemoteMessageBackgroundColor: rgba(240, 243, 247, 0.15);
|
||||
$sidebarWidth: 375px;
|
||||
|
||||
/**
|
||||
* Misc.
|
||||
*/
|
||||
$borderRadius: 4px;
|
||||
$defaultWatermarkLink: '../images/watermark.png';
|
||||
$sidebarWidth: 220px;
|
||||
$popoverMenuPadding: 13px;
|
||||
$happySoftwareBackground: transparent;
|
||||
$desktopAppDragBarHeight: 25px;
|
||||
|
||||
@@ -23,9 +23,14 @@
|
||||
top: 0;
|
||||
width: 100%;
|
||||
|
||||
#largeVideoBackground {
|
||||
min-height: 100%;
|
||||
min-width: 100%;
|
||||
&.fit-full-height #largeVideoBackground {
|
||||
height: 100%;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.fit-full-width #largeVideoBackground {
|
||||
height: auto;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
#largeVideoBackgroundContainer {
|
||||
@@ -509,7 +514,6 @@
|
||||
}
|
||||
#dominantSpeakerAvatar {
|
||||
background-color: #000000;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.dynamic-shadow {
|
||||
@@ -521,37 +525,24 @@
|
||||
transition: box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.avatar-container {
|
||||
.userAvatar {
|
||||
@include maxSize(60px);
|
||||
@include absoluteAligning();
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
height: 50%;
|
||||
overflow: hidden;
|
||||
width: auto;
|
||||
|
||||
.userAvatar {
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
#videoNotAvailableScreen {
|
||||
text-align: center;
|
||||
#avatarContainer {
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
height: 50vh;
|
||||
display:inline-block;
|
||||
margin-top: 25vh;
|
||||
overflow: hidden;
|
||||
width: 50vh;
|
||||
|
||||
#avatar {
|
||||
border-radius: 50%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,10 +162,4 @@ body.welcome-page {
|
||||
font-size: 32px;
|
||||
}
|
||||
}
|
||||
|
||||
.welcome-watermark {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
/**
|
||||
* Let the avatar grow with the tile.
|
||||
*/
|
||||
.avatar-container {
|
||||
.userAvatar {
|
||||
max-height: initial;
|
||||
max-width: initial;
|
||||
}
|
||||
|
||||
@@ -185,7 +185,6 @@
|
||||
font-size: 12px;
|
||||
max-height: 100%;
|
||||
overflow: auto;
|
||||
padding: 15pt;
|
||||
position: absolute;
|
||||
transform: translateY(-50%);
|
||||
top: 50%;
|
||||
|
||||
35
doc/api.md
35
doc/api.md
@@ -192,11 +192,6 @@ The `command` parameter is String object with the name of the command. The follo
|
||||
api.executeCommand('displayName', 'New Nickname');
|
||||
```
|
||||
|
||||
* **password** - Sets the password for the room. This command requires one argument - the password name to be set.
|
||||
```javascript
|
||||
api.executeCommand('password', 'The Password');
|
||||
```
|
||||
|
||||
* **subject** - Sets the subject of the conference. This command requires one argument - the new subject to be set.
|
||||
```javascript
|
||||
api.executeCommand('subject', 'New Conference Subject');
|
||||
@@ -227,11 +222,6 @@ api.executeCommand('toggleChat');
|
||||
api.executeCommand('toggleShareScreen');
|
||||
```
|
||||
|
||||
* **toggleTileView** - Enter / exit tile view layout mode. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('toggleTileView');
|
||||
```
|
||||
|
||||
* **hangup** - Hangups the call. No arguments are required.
|
||||
```javascript
|
||||
api.executeCommand('hangup');
|
||||
@@ -269,14 +259,6 @@ The `event` parameter is a String object with the name of the event.
|
||||
The `listener` parameter is a Function object with one argument that will be notified when the event occurs with data related to the event.
|
||||
|
||||
The following events are currently supported:
|
||||
* **cameraError** - event notifications about Jitsi-Meet having failed to access the camera. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
type: string, // A constant representing the overall type of the error.
|
||||
message: string // Additional information about the error.
|
||||
}
|
||||
```
|
||||
|
||||
* **avatarChanged** - event notifications about avatar
|
||||
changes. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
@@ -300,14 +282,6 @@ changes. The listener will receive an object with the following structure:
|
||||
}
|
||||
```
|
||||
|
||||
* **micError** - event notifications about Jitsi-Meet having failed to access the mic. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
type: string, // A constant representing the overall type of the error.
|
||||
message: string // Additional information about the error.
|
||||
}
|
||||
```
|
||||
|
||||
* **screenSharingStatusChanged** - receives event notifications about turning on/off the local user screen sharing. The listener will receive object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
@@ -322,13 +296,6 @@ changes. The listener will receive an object with the following structure:
|
||||
}
|
||||
```
|
||||
|
||||
* **tileViewChanged** - event notifications about tile view layout mode being entered or exited. The listener will receive object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
enabled: boolean, // whether tile view is not displayed or not
|
||||
}
|
||||
```
|
||||
|
||||
* **incomingMessage** - Event notifications about incoming
|
||||
messages. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
@@ -394,8 +361,6 @@ changes. The listener will receive an object with the following structure:
|
||||
}
|
||||
```
|
||||
|
||||
* **passwordRequired** - event notifications fired when failing to join a room because it has a password.
|
||||
|
||||
* **videoConferenceJoined** - event notifications fired when the local user has joined the video conference. The listener will receive an object with the following structure:
|
||||
```javascript
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ signed Android build for that, that can be a debug self-signed build too, just
|
||||
retrieve the signing hash. The key hash of an already signed ap can be obtained
|
||||
as follows (on macOS): ```keytool -list -printcert -jarfile the-app.apk```
|
||||
- Place the generated ```google-services.json``` file in ```android/app```
|
||||
for Android and the ```GoogleService-Info.plist``` into ```ios/app``` for
|
||||
for Android and the ```GoogleService-Info.plist``` into ```ios/app/src``` for
|
||||
iOS (you can stop at that step, no need for the driver and the code changes they
|
||||
suggest in the wizard).
|
||||
- You may want to exclude these files in YOUR GIT config (do not exclude them in
|
||||
|
||||
47
ios/Podfile
47
ios/Podfile
@@ -14,9 +14,6 @@ end
|
||||
target 'JitsiMeet' do
|
||||
project 'sdk/sdk.xcodeproj'
|
||||
|
||||
# React Native and its dependencies
|
||||
#
|
||||
|
||||
pod 'React', :path => '../node_modules/react-native', :subspecs => [
|
||||
'Core',
|
||||
'CxxBridge',
|
||||
@@ -30,31 +27,33 @@ target 'JitsiMeet' do
|
||||
'RCTWebSocket',
|
||||
]
|
||||
pod 'yoga', :path => '../node_modules/react-native/ReactCommon/yoga'
|
||||
pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
|
||||
pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
|
||||
pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
|
||||
|
||||
# React Native plugins
|
||||
#
|
||||
|
||||
pod 'react-native-background-timer', :path => '../node_modules/react-native-background-timer'
|
||||
pod 'react-native-calendar-events', :path => '../node_modules/react-native-calendar-events'
|
||||
pod 'react-native-fast-image', :path => '../node_modules/react-native-fast-image'
|
||||
pod 'react-native-keep-awake', :path => '../node_modules/react-native-keep-awake'
|
||||
pod 'react-native-webview', :path => '../node_modules/react-native-webview'
|
||||
pod 'react-native-webrtc', :path => '../node_modules/react-native-webrtc'
|
||||
pod 'BVLinearGradient', :path => '../node_modules/react-native-linear-gradient'
|
||||
pod 'RNCAsyncStorage', :path => '../node_modules/@react-native-community/async-storage'
|
||||
pod 'RNGoogleSignin', :path => '../node_modules/react-native-google-signin'
|
||||
pod 'RNSound', :path => '../node_modules/react-native-sound'
|
||||
pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons'
|
||||
pod 'RNWatch', :path => '../node_modules/react-native-watch-connectivity'
|
||||
|
||||
# Native pod dependencies
|
||||
#
|
||||
pod 'DoubleConversion',
|
||||
:podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
|
||||
pod 'glog',
|
||||
:podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
|
||||
pod 'Folly',
|
||||
:podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
|
||||
|
||||
pod 'Amplitude-iOS', '~> 4.0.4'
|
||||
pod 'ObjectiveDropboxOfficial', '~> 3.9.4'
|
||||
|
||||
pod 'react-native-background-timer',
|
||||
:path => '../node_modules/react-native-background-timer'
|
||||
pod 'react-native-fast-image',
|
||||
:path => '../node_modules/react-native-fast-image'
|
||||
pod 'react-native-keep-awake',
|
||||
:path => '../node_modules/react-native-keep-awake'
|
||||
pod 'BVLinearGradient',
|
||||
:path => '../node_modules/react-native-linear-gradient'
|
||||
pod 'react-native-webrtc', :path => '../node_modules/react-native-webrtc'
|
||||
pod 'RNGoogleSignin',
|
||||
:path => '../node_modules/react-native-google-signin'
|
||||
pod 'RNSound', :path => '../node_modules/react-native-sound'
|
||||
pod 'RNVectorIcons', :path => '../node_modules/react-native-vector-icons'
|
||||
pod 'RNWatch', :path => '../node_modules/react-native-watch-connectivity'
|
||||
pod 'react-native-calendar-events',
|
||||
:path => '../node_modules/react-native-calendar-events'
|
||||
end
|
||||
|
||||
post_install do |installer|
|
||||
|
||||
@@ -84,8 +84,8 @@ PODS:
|
||||
- nanopb/decode (0.3.901)
|
||||
- nanopb/encode (0.3.901)
|
||||
- ObjectiveDropboxOfficial (3.9.4)
|
||||
- React (0.59.8):
|
||||
- React/Core (= 0.59.8)
|
||||
- React (0.59.5):
|
||||
- React/Core (= 0.59.5)
|
||||
- react-native-background-timer (2.1.1):
|
||||
- React
|
||||
- react-native-calendar-events (1.6.4):
|
||||
@@ -99,59 +99,55 @@ PODS:
|
||||
- React
|
||||
- react-native-webrtc (1.69.1):
|
||||
- React
|
||||
- react-native-webview (5.8.1):
|
||||
- React
|
||||
- React/Core (0.59.8):
|
||||
- yoga (= 0.59.8.React)
|
||||
- React/CxxBridge (0.59.8):
|
||||
- React/Core (0.59.5):
|
||||
- yoga (= 0.59.5.React)
|
||||
- React/CxxBridge (0.59.5):
|
||||
- Folly (= 2018.10.22.00)
|
||||
- React/Core
|
||||
- React/cxxreact
|
||||
- React/jsiexecutor
|
||||
- React/cxxreact (0.59.8):
|
||||
- React/cxxreact (0.59.5):
|
||||
- boost-for-react-native (= 1.63.0)
|
||||
- DoubleConversion
|
||||
- Folly (= 2018.10.22.00)
|
||||
- glog
|
||||
- React/jsinspector
|
||||
- React/DevSupport (0.59.8):
|
||||
- React/DevSupport (0.59.5):
|
||||
- React/Core
|
||||
- React/RCTWebSocket
|
||||
- React/fishhook (0.59.8)
|
||||
- React/jsi (0.59.8):
|
||||
- React/fishhook (0.59.5)
|
||||
- React/jsi (0.59.5):
|
||||
- DoubleConversion
|
||||
- Folly (= 2018.10.22.00)
|
||||
- glog
|
||||
- React/jsiexecutor (0.59.8):
|
||||
- React/jsiexecutor (0.59.5):
|
||||
- DoubleConversion
|
||||
- Folly (= 2018.10.22.00)
|
||||
- glog
|
||||
- React/cxxreact
|
||||
- React/jsi
|
||||
- React/jsinspector (0.59.8)
|
||||
- React/RCTActionSheet (0.59.8):
|
||||
- React/jsinspector (0.59.5)
|
||||
- React/RCTActionSheet (0.59.5):
|
||||
- React/Core
|
||||
- React/RCTAnimation (0.59.8):
|
||||
- React/RCTAnimation (0.59.5):
|
||||
- React/Core
|
||||
- React/RCTBlob (0.59.8):
|
||||
- React/RCTBlob (0.59.5):
|
||||
- React/Core
|
||||
- React/RCTImage (0.59.8):
|
||||
- React/RCTImage (0.59.5):
|
||||
- React/Core
|
||||
- React/RCTNetwork
|
||||
- React/RCTLinkingIOS (0.59.8):
|
||||
- React/RCTLinkingIOS (0.59.5):
|
||||
- React/Core
|
||||
- React/RCTNetwork (0.59.8):
|
||||
- React/RCTNetwork (0.59.5):
|
||||
- React/Core
|
||||
- React/RCTText (0.59.8):
|
||||
- React/RCTText (0.59.5):
|
||||
- React/Core
|
||||
- React/RCTWebSocket (0.59.8):
|
||||
- React/RCTWebSocket (0.59.5):
|
||||
- React/Core
|
||||
- React/fishhook
|
||||
- React/RCTBlob
|
||||
- RNCAsyncStorage (1.3.4):
|
||||
- React
|
||||
- RNGoogleSignin (2.0.0):
|
||||
- GoogleSignIn (~> 4.4.0)
|
||||
- RNGoogleSignin (1.0.2):
|
||||
- GoogleSignIn
|
||||
- React
|
||||
- RNSound (0.10.12):
|
||||
- React/Core
|
||||
@@ -166,7 +162,7 @@ PODS:
|
||||
- SDWebImage/GIF (4.4.6):
|
||||
- FLAnimatedImage (~> 1.0)
|
||||
- SDWebImage/Core
|
||||
- yoga (0.59.8.React)
|
||||
- yoga (0.59.5.React)
|
||||
|
||||
DEPENDENCIES:
|
||||
- Amplitude-iOS (~> 4.0.4)
|
||||
@@ -184,7 +180,6 @@ DEPENDENCIES:
|
||||
- react-native-fast-image (from `../node_modules/react-native-fast-image`)
|
||||
- react-native-keep-awake (from `../node_modules/react-native-keep-awake`)
|
||||
- react-native-webrtc (from `../node_modules/react-native-webrtc`)
|
||||
- react-native-webview (from `../node_modules/react-native-webview`)
|
||||
- React/Core (from `../node_modules/react-native`)
|
||||
- React/CxxBridge (from `../node_modules/react-native`)
|
||||
- React/DevSupport (from `../node_modules/react-native`)
|
||||
@@ -195,7 +190,6 @@ DEPENDENCIES:
|
||||
- React/RCTNetwork (from `../node_modules/react-native`)
|
||||
- React/RCTText (from `../node_modules/react-native`)
|
||||
- React/RCTWebSocket (from `../node_modules/react-native`)
|
||||
- "RNCAsyncStorage (from `../node_modules/@react-native-community/async-storage`)"
|
||||
- RNGoogleSignin (from `../node_modules/react-native-google-signin`)
|
||||
- RNSound (from `../node_modules/react-native-sound`)
|
||||
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
|
||||
@@ -245,10 +239,6 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-keep-awake"
|
||||
react-native-webrtc:
|
||||
:path: "../node_modules/react-native-webrtc"
|
||||
react-native-webview:
|
||||
:path: "../node_modules/react-native-webview"
|
||||
RNCAsyncStorage:
|
||||
:path: "../node_modules/@react-native-community/async-storage"
|
||||
RNGoogleSignin:
|
||||
:path: "../node_modules/react-native-google-signin"
|
||||
RNSound:
|
||||
@@ -283,21 +273,19 @@ SPEC CHECKSUMS:
|
||||
GTMSessionFetcher: 32aeca0aa144acea523e1c8e053089dec2cb98ca
|
||||
nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48
|
||||
ObjectiveDropboxOfficial: a5afefc83f6467c42c45f2253f583f2ad1ffc701
|
||||
React: 76e6aa2b87d05eb6cccb6926d72685c9a07df152
|
||||
React: 90adac468c7b72bf1fa6c64bf230650f851a8388
|
||||
react-native-background-timer: 0d34748e53a972507c66963490c775321a88f6f2
|
||||
react-native-calendar-events: ee9573e355711ac679e071be70789542431f4ce3
|
||||
react-native-fast-image: 47487b71169aea34868e7b38bf870b6b3f2157c5
|
||||
react-native-keep-awake: eba3137546b10003361b37c761f6c429b59814ae
|
||||
react-native-webrtc: 90a847d19deb2d7323fef8cc89ca12b8995fbc90
|
||||
react-native-webview: a95842e3f351a6d2c8bc8bcc9eab689c7e7e5ad4
|
||||
RNCAsyncStorage: 8e31405a9f12fbf42c2bb330e4560bfd79c18323
|
||||
RNGoogleSignin: d030c6c6591db24c3cee649f64c7babf0a1699a0
|
||||
RNGoogleSignin: 361174d9a3090d295b06257162b560d8efc8a6ed
|
||||
RNSound: e157320f503bdd4f4ee6d8542e948d54f90c3c3a
|
||||
RNVectorIcons: d819334932bcda3332deb3d2c8ea4d069e0b98f9
|
||||
RNWatch: 09738b339eceb66e4d80a2371633ca5fb380fa42
|
||||
SDWebImage: 3f3f0c02f09798048c47a5ed0a13f17b063572d8
|
||||
yoga: 92b2102c3d373d1a790db4ab761d2b0ffc634f64
|
||||
yoga: 2e571f113e8cbeb0eb752aeebc86c1bfe7a8200c
|
||||
|
||||
PODFILE CHECKSUM: b55338cc43312051ed83f8d9c6aadbd8c9402e6a
|
||||
PODFILE CHECKSUM: 9e6bc935ea7d2974604572cc68938281a88cf35c
|
||||
|
||||
COCOAPODS: 1.7.2
|
||||
COCOAPODS: 1.6.1
|
||||
|
||||
@@ -92,7 +92,7 @@ Leaves the currently active conference.
|
||||
|
||||
#### Universal / deep linking
|
||||
|
||||
In order to support Universal / deep linking, `JitsiMeet` offers 2 class
|
||||
In order to support Universal / deep linking, `JitsiMeetView` offers 2 class
|
||||
methods that you app's delegate should call in order for the app to follow those
|
||||
links.
|
||||
|
||||
@@ -104,7 +104,7 @@ is useful when the host application uses other SDKs which also use linking.
|
||||
continueUserActivity:(NSUserActivity *)userActivity
|
||||
restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler
|
||||
{
|
||||
return [[JitsiMeet sharedInstance] application:application
|
||||
return [JitsiMeetView application:application
|
||||
continueUserActivity:userActivity
|
||||
restorationHandler:restorationHandler];
|
||||
}
|
||||
@@ -117,7 +117,7 @@ And also one of the following:
|
||||
- (BOOL)application:(UIApplication *)app
|
||||
openURL:(NSURL *)url
|
||||
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
|
||||
return [[JitsiMeet sharedInstance] application:app
|
||||
return [JitsiMeetView application:app
|
||||
openURL:url
|
||||
options: options];
|
||||
}
|
||||
|
||||
@@ -737,7 +737,7 @@
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = FC967L3QRG;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_BITCODE = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"../../node_modules/react-native-webrtc/ios",
|
||||
|
||||
@@ -46,12 +46,6 @@
|
||||
jitsiMeet.defaultConferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
|
||||
builder.serverURL = [NSURL URLWithString:@"https://meet.jit.si"];
|
||||
builder.welcomePageEnabled = YES;
|
||||
|
||||
// Apple rejected our app because they claim requiring a
|
||||
// Dropbox account for recording is not acceptable.
|
||||
#if DEBUG
|
||||
[builder setFeatureFlag:@"ios.recording.enabled" withBoolean:YES];
|
||||
#endif
|
||||
}];
|
||||
|
||||
[jitsiMeet application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
|
||||
@@ -42,11 +42,11 @@
|
||||
|
||||
- (void)_onJitsiMeetViewDelegateEvent:(NSString *)name
|
||||
withData:(NSDictionary *)data {
|
||||
#if DEBUG
|
||||
NSLog(
|
||||
@"[%s:%d] JitsiMeetViewDelegate %@ %@",
|
||||
__FILE__, __LINE__, name, data);
|
||||
|
||||
#if DEBUG
|
||||
NSAssert(
|
||||
[NSThread isMainThread],
|
||||
@"JitsiMeetViewDelegate %@ method invoked on a non-main thread",
|
||||
@@ -96,10 +96,4 @@
|
||||
[self _onJitsiMeetViewDelegateEvent:@"CONFERENCE_WILL_JOIN" withData:data];
|
||||
}
|
||||
|
||||
#if 0
|
||||
- (void)enterPictureInPicture:(NSDictionary *)data {
|
||||
[self _onJitsiMeetViewDelegateEvent:@"ENTER_PICTURE_IN_PICTURE" withData:data];
|
||||
}
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 4.2 KiB After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.7 KiB After Width: | Height: | Size: 11 KiB |
@@ -14,7 +14,6 @@
|
||||
<objects>
|
||||
<controller title="Meetings" id="AgC-eL-Hgc" customClass="InterfaceController" customModule="JitsiMeetCompanion" customModuleProvider="target">
|
||||
<items>
|
||||
<label alignment="left" textAlignment="left" numberOfLines="0" id="OQN-sx-tDt"/>
|
||||
<table alignment="left" id="gpO-ql-Xsr">
|
||||
<items>
|
||||
<tableRow identifier="MeetingRowType" id="GGl-av-xeJ" customClass="MeetingRowController" customModule="JitsiMeetCompanion_Extension">
|
||||
@@ -40,7 +39,6 @@
|
||||
</table>
|
||||
</items>
|
||||
<connections>
|
||||
<outlet property="infoLabel" destination="OQN-sx-tDt" id="Juv-tb-SNj"/>
|
||||
<outlet property="table" destination="gpO-ql-Xsr" id="aVV-iZ-z3l"/>
|
||||
</connections>
|
||||
</controller>
|
||||
|
||||
@@ -16,31 +16,17 @@
|
||||
*/
|
||||
|
||||
import WatchKit
|
||||
import WatchConnectivity
|
||||
import Foundation
|
||||
|
||||
|
||||
class InterfaceController: WKInterfaceController {
|
||||
|
||||
@IBOutlet var infoLabel: WKInterfaceLabel!
|
||||
@IBOutlet var table: WKInterfaceTable!
|
||||
|
||||
override func didAppear(){
|
||||
self.updateUI(ExtensionDelegate.currentJitsiMeetContext)
|
||||
}
|
||||
|
||||
func updateUI(_ newContext:JitsiMeetContext) {
|
||||
if (newContext.recentURLs == nil || newContext.recentURLs!.count == 0) {
|
||||
infoLabel.setText("There are no recent meetings. Please use the app on the phone to start a new call.")
|
||||
|
||||
table.setHidden(true)
|
||||
infoLabel.setHidden(false)
|
||||
} else {
|
||||
updateRecents(withRecents: newContext.recentURLs!, currentContext: newContext)
|
||||
|
||||
table.setHidden(false)
|
||||
infoLabel.setHidden(true)
|
||||
}
|
||||
if let recentURLsArray = newContext.recentURLs {
|
||||
updateRecents(withRecents: recentURLsArray, currentContext: newContext)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateRecents(withRecents recents: NSArray, currentContext: JitsiMeetContext) {
|
||||
@@ -82,7 +68,7 @@ class InterfaceController: WKInterfaceController {
|
||||
override func contextForSegue(withIdentifier segueIdentifier: String, in table: WKInterfaceTable, rowIndex: Int) -> Any? {
|
||||
let controller = table.rowController(at: rowIndex) as! MeetingRowController
|
||||
let currentContext = ExtensionDelegate.currentJitsiMeetContext
|
||||
|
||||
|
||||
// Copy the current context and add the joinConferenceURL to trigger the command when the in-call screen is displayed
|
||||
let actionContext = JitsiMeetContext(jmContext: currentContext)
|
||||
actionContext.joinConferenceURL = controller.roomUrl
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script will download a bitcode build of the WebRTC framework, if needed.
|
||||
|
||||
if [[ ! "$CONFIGURATION" = "Debug" ]]; then
|
||||
RN_WEBRTC="$SRCROOT/../../node_modules/react-native-webrtc"
|
||||
|
||||
if otool -arch arm64 -l $RN_WEBRTC/ios/WebRTC.framework/WebRTC | grep -q LLVM; then
|
||||
echo "WebRTC framework has bitcode"
|
||||
else
|
||||
echo "WebRTC framework has NO bitcode"
|
||||
$RN_WEBRTC/tools/downloadBitcode.sh
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -5,9 +5,7 @@ set -e -u
|
||||
THIS_DIR=$(cd -P "$(dirname "$(readlink "${BASH_SOURCE[0]}" || echo "${BASH_SOURCE[0]}")")" && pwd)
|
||||
PROJECT_REPO=$(realpath ${THIS_DIR}/../..)
|
||||
RELEASE_REPO=$(realpath ${THIS_DIR}/../../../jitsi-meet-ios-sdk-releases)
|
||||
DEFAULT_SDK_VERSION=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" ${THIS_DIR}/../sdk/src/Info.plist)
|
||||
SDK_VERSION=${OVERRIDE_SDK_VERSION:-${DEFAULT_SDK_VERSION}}
|
||||
DO_GIT_TAG=${GIT_TAG:-0}
|
||||
SDK_VERSION=$(/usr/libexec/PlistBuddy -c "Print CFBundleShortVersionString" ${THIS_DIR}/../sdk/src/Info.plist)
|
||||
|
||||
|
||||
echo "Releasing Jitsi Meet SDK ${SDK_VERSION}"
|
||||
@@ -26,9 +24,7 @@ popd
|
||||
pushd ${PROJECT_REPO}
|
||||
rm -rf ios/sdk/JitsiMeet.framework
|
||||
xcodebuild -workspace ios/jitsi-meet.xcworkspace -scheme JitsiMeet -destination='generic/platform=iOS' -configuration Release archive
|
||||
if [[ $DO_GIT_TAG == 1 ]]; then
|
||||
git tag ios-sdk-${SDK_VERSION}
|
||||
fi
|
||||
git tag -a ios-sdk-${SDK_VERSION}
|
||||
popd
|
||||
|
||||
pushd ${RELEASE_REPO}
|
||||
@@ -37,16 +33,10 @@ pushd ${RELEASE_REPO}
|
||||
cp -r ${PROJECT_REPO}/ios/sdk/JitsiMeet.framework Frameworks/
|
||||
cp -r ${PROJECT_REPO}/node_modules/react-native-webrtc/ios/WebRTC.framework Frameworks/
|
||||
|
||||
# Strip bitcode
|
||||
xcrun bitcode_strip -r Frameworks/JitsiMeet.framework/JitsiMeet -o Frameworks/JitsiMeet.framework/JitsiMeet
|
||||
xcrun bitcode_strip -r Frameworks/WebRTC.framework/WebRTC -o Frameworks/WebRTC.framework/WebRTC
|
||||
|
||||
# Add all files to git
|
||||
if [[ $DO_GIT_TAG == 1 ]]; then
|
||||
git add -A .
|
||||
git commit -m "${SDK_VERSION}"
|
||||
git tag ${SDK_VERSION}
|
||||
fi
|
||||
git add -A .
|
||||
git commit -m "${SDK_VERSION}"
|
||||
git tag ${SDK_VERSION}
|
||||
|
||||
popd
|
||||
|
||||
|
||||
@@ -42,11 +42,8 @@
|
||||
C69EFA0E209A0F660027712B /* JMCallKitListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69EFA0B209A0F660027712B /* JMCallKitListener.swift */; };
|
||||
C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425E204EF76800E062DD /* DragGestureController.swift */; };
|
||||
C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */; };
|
||||
DE762DB422AFDE76000DEBD6 /* JitsiMeetUserInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DE762DB322AFDE76000DEBD6 /* JitsiMeetUserInfo.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
DE762DB622AFDE8D000DEBD6 /* JitsiMeetUserInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = DE762DB522AFDE8D000DEBD6 /* JitsiMeetUserInfo.m */; };
|
||||
DEAD3226220C497000E93636 /* JitsiMeetConferenceOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = DEAD3224220C497000E93636 /* JitsiMeetConferenceOptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
DEAD3227220C497000E93636 /* JitsiMeetConferenceOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = DEAD3225220C497000E93636 /* JitsiMeetConferenceOptions.m */; };
|
||||
DEAFA779229EAD520033A7FA /* RNRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = DEAFA778229EAD520033A7FA /* RNRootView.m */; };
|
||||
DEFC743F21B178FA00E4DD96 /* LocaleDetector.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */; };
|
||||
DEFE535421FB1BF800011A3A /* JitsiMeet.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFE535321FB1BF800011A3A /* JitsiMeet.m */; };
|
||||
DEFE535621FB2E8300011A3A /* ReactUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFE535521FB2E8300011A3A /* ReactUtils.m */; };
|
||||
@@ -96,14 +93,9 @@
|
||||
C6A3425E204EF76800E062DD /* DragGestureController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DragGestureController.swift; sourceTree = "<group>"; };
|
||||
C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiPViewCoordinator.swift; sourceTree = "<group>"; };
|
||||
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
|
||||
DE762DB322AFDE76000DEBD6 /* JitsiMeetUserInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetUserInfo.h; sourceTree = "<group>"; };
|
||||
DE762DB522AFDE8D000DEBD6 /* JitsiMeetUserInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeetUserInfo.m; sourceTree = "<group>"; };
|
||||
DE762DB722AFE166000DEBD6 /* JitsiMeetUserInfo+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetUserInfo+Private.h"; sourceTree = "<group>"; };
|
||||
DEAD3224220C497000E93636 /* JitsiMeetConferenceOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetConferenceOptions.h; sourceTree = "<group>"; };
|
||||
DEAD3225220C497000E93636 /* JitsiMeetConferenceOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeetConferenceOptions.m; sourceTree = "<group>"; };
|
||||
DEAD3228220C734300E93636 /* JitsiMeetConferenceOptions+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetConferenceOptions+Private.h"; sourceTree = "<group>"; };
|
||||
DEAFA777229EAD3B0033A7FA /* RNRootView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNRootView.h; sourceTree = "<group>"; };
|
||||
DEAFA778229EAD520033A7FA /* RNRootView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNRootView.m; sourceTree = "<group>"; };
|
||||
DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LocaleDetector.m; sourceTree = "<group>"; };
|
||||
DEFE535321FB1BF800011A3A /* JitsiMeet.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeet.m; sourceTree = "<group>"; };
|
||||
DEFE535521FB2E8300011A3A /* ReactUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactUtils.m; sourceTree = "<group>"; };
|
||||
@@ -179,13 +171,8 @@
|
||||
DEAD3224220C497000E93636 /* JitsiMeetConferenceOptions.h */,
|
||||
DEAD3228220C734300E93636 /* JitsiMeetConferenceOptions+Private.h */,
|
||||
DEAD3225220C497000E93636 /* JitsiMeetConferenceOptions.m */,
|
||||
DE762DB322AFDE76000DEBD6 /* JitsiMeetUserInfo.h */,
|
||||
DE762DB722AFE166000DEBD6 /* JitsiMeetUserInfo+Private.h */,
|
||||
DE762DB522AFDE8D000DEBD6 /* JitsiMeetUserInfo.m */,
|
||||
0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
|
||||
0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
|
||||
DEAFA777229EAD3B0033A7FA /* RNRootView.h */,
|
||||
DEAFA778229EAD520033A7FA /* RNRootView.m */,
|
||||
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */,
|
||||
0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */,
|
||||
DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */,
|
||||
@@ -267,7 +254,6 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
DE762DB422AFDE76000DEBD6 /* JitsiMeetUserInfo.h in Headers */,
|
||||
0B412F181EDEC65D00B1A0A6 /* JitsiMeetView.h in Headers */,
|
||||
0B93EF7E1EC9DDCD0030D24D /* RCTBridgeWrapper.h in Headers */,
|
||||
0B412F221EDEF6EA00B1A0A6 /* JitsiMeetViewDelegate.h in Headers */,
|
||||
@@ -284,7 +270,6 @@
|
||||
buildConfigurationList = 0BD906ED1EC0C00300C8C18E /* Build configuration list for PBXNativeTarget "JitsiMeet" */;
|
||||
buildPhases = (
|
||||
26796D8589142D80C8AFDA51 /* [CP] Check Pods Manifest.lock */,
|
||||
DE3D81D6228B50FB00A6C149 /* Bitcode */,
|
||||
0BD906E01EC0C00300C8C18E /* Sources */,
|
||||
0BD906E11EC0C00300C8C18E /* Frameworks */,
|
||||
0BD906E21EC0C00300C8C18E /* Headers */,
|
||||
@@ -465,24 +450,6 @@
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet-resources.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
DE3D81D6228B50FB00A6C149 /* Bitcode */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputFileListPaths = (
|
||||
);
|
||||
inputPaths = (
|
||||
);
|
||||
name = Bitcode;
|
||||
outputFileListPaths = (
|
||||
);
|
||||
outputPaths = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "../scripts/bitcode.sh\n";
|
||||
};
|
||||
/* End PBXShellScriptBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@@ -492,8 +459,6 @@
|
||||
files = (
|
||||
0BB9AD7B1F5EC8F4001C08DB /* CallKit.m in Sources */,
|
||||
0BB9AD7D1F60356D001C08DB /* AppInfo.m in Sources */,
|
||||
DEAFA779229EAD520033A7FA /* RNRootView.m in Sources */,
|
||||
DE762DB622AFDE8D000DEBD6 /* JitsiMeetUserInfo.m in Sources */,
|
||||
DEAD3227220C497000E93636 /* JitsiMeetConferenceOptions.m in Sources */,
|
||||
0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
|
||||
0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
|
||||
@@ -650,7 +615,7 @@
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
DYLIB_CURRENT_VERSION = 1;
|
||||
DYLIB_INSTALL_NAME_BASE = "@rpath";
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_BITCODE = YES;
|
||||
INFOPLIST_FILE = src/Info.plist;
|
||||
INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
|
||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
|
||||
|
||||
@@ -14,8 +14,6 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
|
||||
#import "JitsiMeet.h"
|
||||
|
||||
@interface JitsiMeet ()
|
||||
|
||||
@@ -16,9 +16,6 @@
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "JitsiMeetUserInfo.h"
|
||||
|
||||
|
||||
@interface JitsiMeetConferenceOptionsBuilder : NSObject
|
||||
|
||||
/**
|
||||
@@ -29,10 +26,6 @@
|
||||
* Room name.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) NSString *room;
|
||||
/**
|
||||
* Conference subject.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) NSString *subject;
|
||||
/**
|
||||
* JWT token used for authentication.
|
||||
*/
|
||||
@@ -44,11 +37,6 @@
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) NSDictionary *colorScheme;
|
||||
|
||||
/**
|
||||
* Feature flags. See: https://github.com/jitsi/jitsi-meet/blob/master/react/features/base/flags/constants.js
|
||||
*/
|
||||
@property (nonatomic, readonly, nonnull) NSDictionary *featureFlags;
|
||||
|
||||
/**
|
||||
* Set to YES to join the conference with audio / video muted or to start in audio
|
||||
* only mode respectively.
|
||||
@@ -63,26 +51,15 @@
|
||||
*/
|
||||
@property (nonatomic) BOOL welcomePageEnabled;
|
||||
|
||||
/**
|
||||
* Information about the local user. It will be used in absence of a token.
|
||||
*/
|
||||
@property (nonatomic, nullable) JitsiMeetUserInfo *userInfo;
|
||||
|
||||
- (void)setFeatureFlag:(NSString *_Nonnull)flag withBoolean:(BOOL)value;
|
||||
- (void)setFeatureFlag:(NSString *_Nonnull)flag withValue:(id _Nonnull)value;
|
||||
|
||||
@end
|
||||
|
||||
@interface JitsiMeetConferenceOptions : NSObject
|
||||
|
||||
@property (nonatomic, copy, nullable, readonly) NSURL *serverURL;
|
||||
|
||||
@property (nonatomic, copy, nullable, readonly) NSString *room;
|
||||
@property (nonatomic, copy, nullable, readonly) NSString *subject;
|
||||
@property (nonatomic, copy, nullable, readonly) NSString *token;
|
||||
|
||||
@property (nonatomic, copy, nullable) NSDictionary *colorScheme;
|
||||
@property (nonatomic, readonly, nonnull) NSDictionary *featureFlags;
|
||||
|
||||
@property (nonatomic, readonly) BOOL audioOnly;
|
||||
@property (nonatomic, readonly) BOOL audioMuted;
|
||||
@@ -90,8 +67,6 @@
|
||||
|
||||
@property (nonatomic, readonly) BOOL welcomePageEnabled;
|
||||
|
||||
@property (nonatomic, nullable) JitsiMeetUserInfo *userInfo;
|
||||
|
||||
+ (instancetype _Nonnull)fromBuilder:(void (^_Nonnull)(JitsiMeetConferenceOptionsBuilder *_Nonnull))initBlock;
|
||||
- (instancetype _Nonnull)init NS_UNAVAILABLE;
|
||||
|
||||
|
||||
@@ -17,19 +17,12 @@
|
||||
#import <React/RCTUtils.h>
|
||||
|
||||
#import "JitsiMeetConferenceOptions+Private.h"
|
||||
#import "JitsiMeetUserInfo+Private.h"
|
||||
|
||||
/**
|
||||
* Backwards compatibility: turn the boolean property into a feature flag.
|
||||
*/
|
||||
static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
|
||||
|
||||
@implementation JitsiMeetConferenceOptionsBuilder {
|
||||
NSNumber *_audioOnly;
|
||||
NSNumber *_audioMuted;
|
||||
NSNumber *_videoMuted;
|
||||
NSMutableDictionary *_featureFlags;
|
||||
NSNumber *_welcomePageEnabled;
|
||||
}
|
||||
|
||||
@dynamic audioOnly;
|
||||
@@ -41,30 +34,20 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
if (self = [super init]) {
|
||||
_serverURL = nil;
|
||||
_room = nil;
|
||||
_subject = nil;
|
||||
_token = nil;
|
||||
|
||||
_colorScheme = nil;
|
||||
_featureFlags = [[NSMutableDictionary alloc] init];
|
||||
|
||||
_audioOnly = nil;
|
||||
_audioMuted = nil;
|
||||
_videoMuted = nil;
|
||||
|
||||
_userInfo = nil;
|
||||
_welcomePageEnabled = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)setFeatureFlag:(NSString *)flag withBoolean:(BOOL)value {
|
||||
[self setFeatureFlag:flag withValue:[NSNumber numberWithBool:value]];
|
||||
}
|
||||
|
||||
- (void)setFeatureFlag:(NSString *)flag withValue:(id)value {
|
||||
_featureFlags[flag] = value;
|
||||
}
|
||||
|
||||
#pragma mark - Dynamic properties
|
||||
|
||||
- (void)setAudioOnly:(BOOL)audioOnly {
|
||||
@@ -92,14 +75,11 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
}
|
||||
|
||||
- (void)setWelcomePageEnabled:(BOOL)welcomePageEnabled {
|
||||
[self setFeatureFlag:WelcomePageEnabledFeatureFlag
|
||||
withBoolean:welcomePageEnabled];
|
||||
_welcomePageEnabled = [NSNumber numberWithBool:welcomePageEnabled];
|
||||
}
|
||||
|
||||
- (BOOL)welcomePageEnabled {
|
||||
NSNumber *n = _featureFlags[WelcomePageEnabledFeatureFlag];
|
||||
|
||||
return n != nil ? [n boolValue] : NO;
|
||||
return _welcomePageEnabled && [_welcomePageEnabled boolValue];
|
||||
}
|
||||
|
||||
#pragma mark - Private API
|
||||
@@ -116,13 +96,17 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
return _videoMuted;
|
||||
}
|
||||
|
||||
- (NSNumber *)getWelcomePageEnabled {
|
||||
return _welcomePageEnabled;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation JitsiMeetConferenceOptions {
|
||||
NSNumber *_audioOnly;
|
||||
NSNumber *_audioMuted;
|
||||
NSNumber *_videoMuted;
|
||||
NSDictionary *_featureFlags;
|
||||
NSNumber *_welcomePageEnabled;
|
||||
}
|
||||
|
||||
@dynamic audioOnly;
|
||||
@@ -145,9 +129,7 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
}
|
||||
|
||||
- (BOOL)welcomePageEnabled {
|
||||
NSNumber *n = _featureFlags[WelcomePageEnabledFeatureFlag];
|
||||
|
||||
return n != nil ? [n boolValue] : NO;
|
||||
return _welcomePageEnabled && [_welcomePageEnabled boolValue];
|
||||
}
|
||||
|
||||
#pragma mark - Internal initializer
|
||||
@@ -156,7 +138,6 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
if (self = [super init]) {
|
||||
_serverURL = builder.serverURL;
|
||||
_room = builder.room;
|
||||
_subject = builder.subject;
|
||||
_token = builder.token;
|
||||
|
||||
_colorScheme = builder.colorScheme;
|
||||
@@ -165,9 +146,7 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
_audioMuted = [builder getAudioMuted];
|
||||
_videoMuted = [builder getVideoMuted];
|
||||
|
||||
_featureFlags = [NSDictionary dictionaryWithDictionary:builder.featureFlags];
|
||||
|
||||
_userInfo = builder.userInfo;
|
||||
_welcomePageEnabled = [builder getWelcomePageEnabled];
|
||||
}
|
||||
|
||||
return self;
|
||||
@@ -186,12 +165,14 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
- (NSDictionary *)asProps {
|
||||
NSMutableDictionary *props = [[NSMutableDictionary alloc] init];
|
||||
|
||||
props[@"flags"] = [NSMutableDictionary dictionaryWithDictionary:_featureFlags];
|
||||
|
||||
if (_colorScheme != nil) {
|
||||
props[@"colorScheme"] = self.colorScheme;
|
||||
}
|
||||
|
||||
if (_welcomePageEnabled != nil) {
|
||||
props[@"welcomePageEnabled"] = @(self.welcomePageEnabled);
|
||||
}
|
||||
|
||||
NSMutableDictionary *config = [[NSMutableDictionary alloc] init];
|
||||
if (_audioOnly != nil) {
|
||||
config[@"startAudioOnly"] = @(self.audioOnly);
|
||||
@@ -202,9 +183,6 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
if (_videoMuted != nil) {
|
||||
config[@"startWithVideoMuted"] = @(self.videoMuted);
|
||||
}
|
||||
if (_subject != nil) {
|
||||
config[@"subject"] = self.subject;
|
||||
}
|
||||
|
||||
NSMutableDictionary *urlProps = [[NSMutableDictionary alloc] init];
|
||||
|
||||
@@ -225,10 +203,6 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled";
|
||||
urlProps[@"jwt"] = _token;
|
||||
}
|
||||
|
||||
if (_token == nil && _userInfo != nil) {
|
||||
props[@"userInfo"] = [self.userInfo asDict];
|
||||
}
|
||||
|
||||
urlProps[@"config"] = config;
|
||||
props[@"url"] = urlProps;
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "JitsiMeetUserInfo.h"
|
||||
|
||||
@interface JitsiMeetUserInfo ()
|
||||
|
||||
- (NSMutableDictionary *)asDict;
|
||||
|
||||
@end
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface JitsiMeetUserInfo : NSObject
|
||||
|
||||
/**
|
||||
* User display name.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) NSString *displayName;
|
||||
/**
|
||||
* User e-mail.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) NSString *email;
|
||||
/**
|
||||
* URL for the user avatar.
|
||||
*/
|
||||
@property (nonatomic, copy, nullable) NSURL *avatar;
|
||||
|
||||
- (instancetype _Nullable)initWithDisplayName:(NSString *_Nullable)displayName
|
||||
andEmail:(NSString *_Nullable)email
|
||||
andAvatar:(NSURL *_Nullable) avatar;
|
||||
|
||||
@end
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "JitsiMeetUserInfo+Private.h"
|
||||
|
||||
@implementation JitsiMeetUserInfo
|
||||
|
||||
- (instancetype)initWithDisplayName:(NSString *)displayName
|
||||
andEmail:(NSString *)email
|
||||
andAvatar:(NSURL *_Nullable) avatar {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.displayName = displayName;
|
||||
self.email = email;
|
||||
self.avatar = avatar;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSDictionary *)asDict {
|
||||
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
|
||||
|
||||
if (self.displayName != nil) {
|
||||
dict[@"displayName"] = self.displayName;
|
||||
}
|
||||
|
||||
if (self.email != nil) {
|
||||
dict[@"email"] = self.email;
|
||||
}
|
||||
|
||||
if (self.avatar != nil) {
|
||||
NSString *avatarURL = [self.avatar absoluteString];
|
||||
if (avatarURL != nil) {
|
||||
dict[@"avatarURL"] = avatarURL;
|
||||
}
|
||||
}
|
||||
|
||||
return dict;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -17,17 +17,12 @@
|
||||
|
||||
#include <mach/mach_time.h>
|
||||
|
||||
#import <React/RCTRootView.h>
|
||||
|
||||
#import "JitsiMeet+Private.h"
|
||||
#import "JitsiMeetConferenceOptions+Private.h"
|
||||
#import "JitsiMeetView+Private.h"
|
||||
#import "ReactUtils.h"
|
||||
#import "RNRootView.h"
|
||||
|
||||
|
||||
/**
|
||||
* Backwards compatibility: turn the boolean prop into a feature flag.
|
||||
*/
|
||||
static NSString *const PiPEnabledFeatureFlag = @"pip.enabled";
|
||||
|
||||
|
||||
@implementation JitsiMeetView {
|
||||
@@ -41,7 +36,7 @@ static NSString *const PiPEnabledFeatureFlag = @"pip.enabled";
|
||||
/**
|
||||
* React Native view where the entire content will be rendered.
|
||||
*/
|
||||
RNRootView *rootView;
|
||||
RCTRootView *rootView;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,16 +123,12 @@ static void initializeViewsMap() {
|
||||
- (void)setProps:(NSDictionary *_Nonnull)newProps {
|
||||
NSMutableDictionary *props = mergeProps([[JitsiMeet sharedInstance] getDefaultProps], newProps);
|
||||
|
||||
// Set the PiP flag if it wasn't manually set.
|
||||
NSMutableDictionary *featureFlags = props[@"flags"];
|
||||
if (featureFlags[PiPEnabledFeatureFlag] == nil) {
|
||||
featureFlags[PiPEnabledFeatureFlag]
|
||||
= [NSNumber numberWithBool:
|
||||
self.delegate && [self.delegate respondsToSelector:@selector(enterPictureInPicture:)]];
|
||||
}
|
||||
|
||||
props[@"externalAPIScope"] = externalAPIScope;
|
||||
|
||||
// TODO: put this in some 'flags' field
|
||||
props[@"pictureInPictureEnabled"]
|
||||
= @(self.delegate && [self.delegate respondsToSelector:@selector(enterPictureInPicture:)]);
|
||||
|
||||
// This method is supposed to be imperative i.e. a second
|
||||
// invocation with one and the same URL is expected to join the respective
|
||||
// conference again if the first invocation was followed by leaving the
|
||||
@@ -154,9 +145,9 @@ static void initializeViewsMap() {
|
||||
} else {
|
||||
RCTBridge *bridge = [[JitsiMeet sharedInstance] getReactBridge];
|
||||
rootView
|
||||
= [[RNRootView alloc] initWithBridge:bridge
|
||||
moduleName:@"App"
|
||||
initialProperties:props];
|
||||
= [[RCTRootView alloc] initWithBridge:bridge
|
||||
moduleName:@"App"
|
||||
initialProperties:props];
|
||||
rootView.backgroundColor = self.backgroundColor;
|
||||
|
||||
// Add rootView as a subview which completely covers this one.
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <React/RCTRootView.h>
|
||||
|
||||
@interface RNRootView : RCTRootView
|
||||
@end
|
||||
@@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <React/RCTRootContentView.h>
|
||||
#import <React/RCTLog.h>
|
||||
|
||||
#import "RNRootView.h"
|
||||
|
||||
@implementation RNRootView
|
||||
|
||||
// Monkey-patch RCTRootView.runApplication to avoid logging initial props.
|
||||
- (void)runApplication:(RCTBridge *)bridge
|
||||
{
|
||||
NSString *moduleName = [self valueForKey:@"_moduleName"] ?: @"";
|
||||
RCTRootContentView *_contentView = [self valueForKey:@"_contentView"];
|
||||
NSNumber *reactTag = [_contentView valueForKey:@"reactTag"];
|
||||
|
||||
NSDictionary *appParameters = @{
|
||||
@"rootTag": reactTag,
|
||||
@"initialProps": self.appProperties ?: @{},
|
||||
};
|
||||
#if DEBUG
|
||||
RCTLogInfo(@"Running application %@ (%@)", moduleName, appParameters);
|
||||
#endif
|
||||
|
||||
[bridge enqueueJSCall:@"AppRegistry"
|
||||
method:@"runApplication"
|
||||
args:@[moduleName, appParameters]
|
||||
completion:NULL];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -33,10 +33,6 @@ RCT_EXPORT_METHOD(init:(NSString*)instanceName API_KEY:(NSString*)apiKey) {
|
||||
[[Amplitude instanceWithName:instanceName] initializeApiKey:apiKey];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setUserId:(NSString*)instanceName userId: (NSString *) userId) {
|
||||
[[Amplitude instanceWithName:instanceName] setUserId:userId];
|
||||
}
|
||||
|
||||
RCT_EXPORT_METHOD(setUserProperties:(NSString*)instanceName userPropsString:(NSDictionary*)userProps) {
|
||||
if (userProps != nil) {
|
||||
[[Amplitude instanceWithName:instanceName] setUserProperties:userProps];
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTEventEmitter.h>
|
||||
#import <React/RCTUtils.h>
|
||||
#import <WebRTC/WebRTC.h>
|
||||
|
||||
#import <JitsiMeet/JitsiMeet-Swift.h>
|
||||
|
||||
@@ -74,12 +73,6 @@ RCT_EXPORT_METHOD(endCall:(NSString *)callUUID
|
||||
#endif
|
||||
|
||||
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
||||
|
||||
if (!callUUID_) {
|
||||
reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
|
||||
return;
|
||||
}
|
||||
|
||||
CXEndCallAction *action
|
||||
= [[CXEndCallAction alloc] initWithCallUUID:callUUID_];
|
||||
[self requestTransaction:[[CXTransaction alloc] initWithAction:action]
|
||||
@@ -97,12 +90,6 @@ RCT_EXPORT_METHOD(setMuted:(NSString *)callUUID
|
||||
#endif
|
||||
|
||||
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
||||
|
||||
if (!callUUID_) {
|
||||
reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
|
||||
return;
|
||||
}
|
||||
|
||||
CXSetMutedCallAction *action
|
||||
= [[CXSetMutedCallAction alloc] initWithCallUUID:callUUID_ muted:muted];
|
||||
[self requestTransaction:[[CXTransaction alloc] initWithAction:action]
|
||||
@@ -135,13 +122,6 @@ RCT_EXPORT_METHOD(startCall:(NSString *)callUUID
|
||||
NSLog(@"[RNCallKit][startCall] callUUID = %@", callUUID);
|
||||
#endif
|
||||
|
||||
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
||||
|
||||
if (!callUUID_) {
|
||||
reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't start a new call if there's an active call for the specified
|
||||
// callUUID. JitsiMeetView was configured for an incoming call.
|
||||
if ([JMCallKitProxy hasActiveCallForUUID:callUUID]) {
|
||||
@@ -151,6 +131,7 @@ RCT_EXPORT_METHOD(startCall:(NSString *)callUUID
|
||||
|
||||
CXHandle *handle_
|
||||
= [[CXHandle alloc] initWithType:CXHandleTypeGeneric value:handle];
|
||||
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
||||
CXStartCallAction *action
|
||||
= [[CXStartCallAction alloc] initWithCallUUID:callUUID_
|
||||
handle:handle_];
|
||||
@@ -164,12 +145,6 @@ RCT_EXPORT_METHOD(reportCallFailed:(NSString *)callUUID
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
reject:(RCTPromiseRejectBlock)reject) {
|
||||
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
||||
|
||||
if (!callUUID_) {
|
||||
reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
|
||||
return;
|
||||
}
|
||||
|
||||
[JMCallKitProxy reportCallWith:callUUID_
|
||||
endedAt:nil
|
||||
reason:CXCallEndedReasonFailed];
|
||||
@@ -181,12 +156,6 @@ RCT_EXPORT_METHOD(reportConnectedOutgoingCall:(NSString *)callUUID
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
reject:(RCTPromiseRejectBlock)reject) {
|
||||
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
||||
|
||||
if (!callUUID_) {
|
||||
reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
|
||||
return;
|
||||
}
|
||||
|
||||
[JMCallKitProxy reportOutgoingCallWith:callUUID_
|
||||
connectedAt:nil];
|
||||
resolve(nil);
|
||||
@@ -205,12 +174,6 @@ RCT_EXPORT_METHOD(updateCall:(NSString *)callUUID
|
||||
#endif
|
||||
|
||||
NSUUID *callUUID_ = [[NSUUID alloc] initWithUUIDString:callUUID];
|
||||
|
||||
if (!callUUID_) {
|
||||
reject(nil, [NSString stringWithFormat:@"Invalid UUID: %@", callUUID], nil);
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *displayName = options[@"displayName"];
|
||||
BOOL hasVideo = [(NSNumber*)options[@"hasVideo"] boolValue];
|
||||
|
||||
@@ -344,35 +307,21 @@ RCT_EXPORT_METHOD(updateCall:(NSString *)callUUID
|
||||
startedConnectingAt:nil];
|
||||
}
|
||||
|
||||
- (void) providerDidActivateAudioSessionWithSession:(AVAudioSession *)session {
|
||||
// The following just help with debugging:
|
||||
#ifdef DEBUG
|
||||
|
||||
- (void) providerDidActivateAudioSessionWithSession:(AVAudioSession *)session {
|
||||
NSLog(@"[RNCallKit][CXProviderDelegate][provider:didActivateAudioSession:]");
|
||||
#endif
|
||||
[[RTCAudioSession sharedInstance] audioSessionDidActivate:session];
|
||||
}
|
||||
|
||||
- (void) providerDidDeactivateAudioSessionWithSession:(AVAudioSession *)session {
|
||||
#ifdef DEBUG
|
||||
NSLog(@"[RNCallKit][CXProviderDelegate][provider:didDeactivateAudioSession:]");
|
||||
#endif
|
||||
[[RTCAudioSession sharedInstance] audioSessionDidDeactivate:session];
|
||||
}
|
||||
|
||||
- (void) providerTimedOutPerformingActionWithAction:(CXAction *)action {
|
||||
#ifdef DEBUG
|
||||
NSLog(@"[RNCallKit][CXProviderDelegate][provider:timedOutPerformingAction:]");
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
// The bridge might already be invalidated by the time a CallKit event is processed,
|
||||
// just ignore it and don't emit it.
|
||||
- (void)sendEventWithName:(NSString *)name body:(id)body {
|
||||
if (!self.bridge) {
|
||||
return;
|
||||
}
|
||||
|
||||
[super sendEventWithName:name body:body];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -21,7 +21,6 @@ import Foundation
|
||||
internal final class JMCallKitEmitter: NSObject, CXProviderDelegate {
|
||||
|
||||
private let listeners = NSMutableArray()
|
||||
private var pendingMuteActions = Set<UUID>()
|
||||
|
||||
internal override init() {}
|
||||
|
||||
@@ -37,12 +36,6 @@ internal final class JMCallKitEmitter: NSObject, CXProviderDelegate {
|
||||
listeners.remove(listener)
|
||||
}
|
||||
|
||||
// MARK: - Add mute action
|
||||
|
||||
func addMuteAction(_ actionUUID: UUID) {
|
||||
pendingMuteActions.insert(actionUUID)
|
||||
}
|
||||
|
||||
// MARK: - CXProviderDelegate
|
||||
|
||||
func providerDidReset(_ provider: CXProvider) {
|
||||
@@ -50,7 +43,6 @@ internal final class JMCallKitEmitter: NSObject, CXProviderDelegate {
|
||||
let listener = $0 as! JMCallKitListener
|
||||
listener.providerDidReset?()
|
||||
}
|
||||
pendingMuteActions.removeAll()
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
|
||||
@@ -72,20 +64,9 @@ internal final class JMCallKitEmitter: NSObject, CXProviderDelegate {
|
||||
}
|
||||
|
||||
func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
|
||||
let uuid = pendingMuteActions.remove(action.uuid)
|
||||
|
||||
// Avoid mute actions ping-pong: if the mute action was caused by
|
||||
// the JS side (we requested a transaction) don't call the delegate
|
||||
// method. If it was called by the provder itself (when the user presses
|
||||
// the mute button in the CallKit view) then call the delegate method.
|
||||
//
|
||||
// NOTE: don't try to be clever and remove this. Been there, done that.
|
||||
// Won't work.
|
||||
if (uuid == nil) {
|
||||
listeners.forEach {
|
||||
let listener = $0 as! JMCallKitListener
|
||||
listener.performSetMutedCall?(UUID: action.callUUID, isMuted: action.isMuted)
|
||||
}
|
||||
listeners.forEach {
|
||||
let listener = $0 as! JMCallKitListener
|
||||
listener.performSetMutedCall?(UUID: action.callUUID, isMuted: action.isMuted)
|
||||
}
|
||||
|
||||
action.fulfill()
|
||||
|
||||
@@ -160,14 +160,6 @@ import Foundation
|
||||
completion: @escaping (Error?) -> Swift.Void) {
|
||||
guard enabled else { return }
|
||||
|
||||
// XXX keep track of muted actions to avoid "ping-pong"ing. See
|
||||
// JMCallKitEmitter for details on the CXSetMutedCallAction handling.
|
||||
for action in transaction.actions {
|
||||
if (action as? CXSetMutedCallAction) != nil {
|
||||
emitter.addMuteAction(action.uuid)
|
||||
}
|
||||
}
|
||||
|
||||
callController.request(transaction, completion: completion)
|
||||
}
|
||||
|
||||
@@ -195,4 +187,3 @@ import Foundation
|
||||
return update
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,15 +37,6 @@ public class PiPViewCoordinator {
|
||||
}
|
||||
}
|
||||
|
||||
public enum Position {
|
||||
case lowerRightCorner
|
||||
case upperRightCorner
|
||||
case lowerLeftCorner
|
||||
case upperLeftCorner
|
||||
}
|
||||
|
||||
public var initialPositionInSuperview = Position.lowerRightCorner
|
||||
|
||||
/// The size ratio of the view when in PiP mode
|
||||
public var pipSizeRatio: CGFloat = {
|
||||
let deviceIdiom = UIScreen.main.traitCollection.userInterfaceIdiom
|
||||
@@ -214,21 +205,9 @@ public class PiPViewCoordinator {
|
||||
let adjustedBounds = bounds.inset(by: dragBoundInsets)
|
||||
let size = CGSize(width: bounds.size.width * pipSizeRatio,
|
||||
height: bounds.size.height * pipSizeRatio)
|
||||
let origin = initialPositionFor(pipSize: size, bounds: adjustedBounds)
|
||||
return CGRect(x: origin.x, y: origin.y, width: size.width, height: size.height)
|
||||
}
|
||||
|
||||
private func initialPositionFor(pipSize size: CGSize, bounds: CGRect) -> CGPoint {
|
||||
switch initialPositionInSuperview {
|
||||
case .lowerLeftCorner:
|
||||
return CGPoint(x: bounds.minX, y: bounds.maxY - size.height)
|
||||
case .lowerRightCorner:
|
||||
return CGPoint(x: bounds.maxX - size.width, y: bounds.maxY - size.height)
|
||||
case .upperLeftCorner:
|
||||
return CGPoint(x: bounds.minX, y: bounds.minY)
|
||||
case .upperRightCorner:
|
||||
return CGPoint(x: bounds.maxX - size.width, y: bounds.minY)
|
||||
}
|
||||
let x: CGFloat = adjustedBounds.maxX - size.width
|
||||
let y: CGFloat = adjustedBounds.maxY - size.height
|
||||
return CGRect(x: x, y: y, width: size.width, height: size.height)
|
||||
}
|
||||
|
||||
// MARK: - Animation helpers
|
||||
|
||||
@@ -98,13 +98,11 @@ curl -L -o ${CERT_DIR}/AppleWWDRCA.cer 'http://developer.apple.com/certification
|
||||
curl -L -o ${CERT_DIR}/dev-cert.cer.enc ${IOS_DEV_CERT_URL}
|
||||
curl -L -o ${CERT_DIR}/dev-key.p12.enc ${IOS_DEV_CERT_KEY_URL}
|
||||
curl -L -o ${CERT_DIR}/dev-profile.mobileprovision.enc ${IOS_DEV_PROV_PROFILE_URL}
|
||||
curl -L -o ${CERT_DIR}/dev-watch-profile.mobileprovision.enc ${IOS_DEV_WATCH_PROV_PROFILE_URL}
|
||||
curl -L -o ${CERT_DIR}/id_rsa.enc ${DEPLOY_SSH_CERT_URL}
|
||||
|
||||
openssl aes-256-cbc -k "$ENCRYPTION_PASSWORD" -in ${CERT_DIR}/dev-cert.cer.enc -d -a -out ${CERT_DIR}/dev-cert.cer
|
||||
openssl aes-256-cbc -k "$ENCRYPTION_PASSWORD" -in ${CERT_DIR}/dev-key.p12.enc -d -a -out ${CERT_DIR}/dev-key.p12
|
||||
openssl aes-256-cbc -k "$ENCRYPTION_PASSWORD" -in ${CERT_DIR}/dev-profile.mobileprovision.enc -d -a -out ${CERT_DIR}/dev-profile.mobileprovision
|
||||
openssl aes-256-cbc -k "$ENCRYPTION_PASSWORD" -in ${CERT_DIR}/dev-watch-profile.mobileprovision.enc -d -a -out ${CERT_DIR}/dev-watch-profile.mobileprovision
|
||||
openssl aes-256-cbc -k "$ENCRYPTION_PASSWORD" -in ${CERT_DIR}/id_rsa.enc -d -a -out ${CERT_DIR}/id_rsa
|
||||
chmod 0600 ${CERT_DIR}/id_rsa
|
||||
|
||||
@@ -128,13 +126,9 @@ echo "done set-key-partition-list"
|
||||
mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
|
||||
|
||||
cp "${CERT_DIR}/dev-profile.mobileprovision" ~/Library/MobileDevice/Provisioning\ Profiles/
|
||||
cp "${CERT_DIR}/dev-watch-profile.mobileprovision" ~/Library/MobileDevice/Provisioning\ Profiles/
|
||||
|
||||
npm install
|
||||
|
||||
# Ever since the Apple Watch app has been added the bitcode for WebRTC needs to be downloaded in order to build successfully
|
||||
./node_modules/react-native-webrtc/tools/downloadBitcode.sh
|
||||
|
||||
cd ios
|
||||
pod update
|
||||
pod install
|
||||
@@ -152,12 +146,4 @@ xcodebuild -quiet -exportArchive -archivePath /tmp/jitsi-meet/jitsi-meet.xcarchi
|
||||
|
||||
echo "Will try deploy the .ipa to: ${IPA_DEPLOY_LOCATION}"
|
||||
|
||||
ssh-add ${CERT_DIR}/id_rsa
|
||||
|
||||
if [ ! -z ${SCP_PROXY_HOST} ];
|
||||
then
|
||||
scp -o ProxyCommand="ssh -t -A -l %r ${SCP_PROXY_HOST} -o \"StrictHostKeyChecking no\" -o \"BatchMode yes\" -W %h:%p" -o StrictHostKeyChecking=no -o LogLevel=DEBUG "${IPA_EXPORT_DIR}/jitsi-meet.ipa" "${IPA_DEPLOY_LOCATION}"
|
||||
else
|
||||
scp -o StrictHostKeyChecking=no -o LogLevel=DEBUG "${IPA_EXPORT_DIR}/jitsi-meet.ipa" "${IPA_DEPLOY_LOCATION}"
|
||||
fi
|
||||
|
||||
scp -i ${CERT_DIR}/id_rsa -o StrictHostKeyChecking=no -o LogLevel=DEBUG "${IPA_EXPORT_DIR}/jitsi-meet.ipa" "${IPA_DEPLOY_LOCATION}"
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
{
|
||||
"en": "Английски",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "Български",
|
||||
"cs": "",
|
||||
"de": "Немски",
|
||||
"el": "",
|
||||
"eo": "Есперанто",
|
||||
"es": "Испански",
|
||||
"fr": "Френски",
|
||||
"hy": "Арменски",
|
||||
"it": "Италиански",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "Норвежки букмол",
|
||||
"oc": "Окситански",
|
||||
"pl": "Полски",
|
||||
"ptBR": "Португалски (Бразилия)",
|
||||
@@ -22,6 +14,7 @@
|
||||
"sl": "Словенски",
|
||||
"sv": "Шведски",
|
||||
"tr": "Турски",
|
||||
"vi": "",
|
||||
"zhCN": "Китайски (Китай)"
|
||||
"zhCN": "Китайски (Китай)",
|
||||
"nb": "Норвежки букмол",
|
||||
"eo": "Есперанто"
|
||||
}
|
||||
@@ -1,18 +1,11 @@
|
||||
{
|
||||
"en": "Englisch",
|
||||
"az": "",
|
||||
"bg": "Bulgarisch",
|
||||
"cs": "",
|
||||
"de": "Deutsch",
|
||||
"el": "",
|
||||
"eo": "Esperanto",
|
||||
"es": "Spanisch",
|
||||
"fr": "Französisch",
|
||||
"hy": "Armenisch",
|
||||
"it": "Italienisch",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "Norwegisch (Bokmal)",
|
||||
"oc": "Okzitanisch",
|
||||
"pl": "Polnisch",
|
||||
"ptBR": "Portugiesisch (Brasilien)",
|
||||
@@ -21,6 +14,7 @@
|
||||
"sl": "Slowenisch",
|
||||
"sv": "Schwedisch",
|
||||
"tr": "Türkisch",
|
||||
"vi": "",
|
||||
"zhCN": "Chinesisch (China)"
|
||||
"zhCN": "Chinesisch (China)",
|
||||
"nb": "Norwegisch (Bokmal)",
|
||||
"eo": "Esperanto"
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
{
|
||||
"en": "",
|
||||
"af": "",
|
||||
"az": "",
|
||||
"bg": "",
|
||||
"cs": "",
|
||||
"de": "",
|
||||
"el": "",
|
||||
"eo": "",
|
||||
"es": "",
|
||||
"fr": "",
|
||||
"hy": "",
|
||||
"it": "",
|
||||
"ja": "",
|
||||
"ko": "",
|
||||
"nb": "",
|
||||
"oc": "",
|
||||
"pl": "",
|
||||
"ptBR": "",
|
||||
"ru": "",
|
||||
"sk": "",
|
||||
"sl": "",
|
||||
"sv": "",
|
||||
"tr": "",
|
||||
"vi": "",
|
||||
"zhCN": ""
|
||||
}
|
||||
@@ -1,19 +1,11 @@
|
||||
{
|
||||
"en": "Anglés",
|
||||
"af": "Afrikaans",
|
||||
"az": "Azèri",
|
||||
"bg": "Bulgar",
|
||||
"cs": "Chèc",
|
||||
"de": "Aleman",
|
||||
"el": "Grèc",
|
||||
"eo": "Esperanto",
|
||||
"es": "Castelhan",
|
||||
"fr": "Francés",
|
||||
"hy": "Armenian",
|
||||
"it": "Italian",
|
||||
"ja": "Japonés",
|
||||
"ko": "Corean",
|
||||
"nb": "Norvegian Bokmål",
|
||||
"oc": "Occitan",
|
||||
"pl": "Polonés",
|
||||
"ptBR": "Portugués (Brasil)",
|
||||
@@ -22,6 +14,7 @@
|
||||
"sl": "Eslovèn",
|
||||
"sv": "Suedés",
|
||||
"tr": "Turc",
|
||||
"vi": "Vietnamian",
|
||||
"zhCN": "Chinés (China)"
|
||||
"zhCN": "Chinés (China)",
|
||||
"nb": "Norvegian Bokmål",
|
||||
"eo": "Esperanto"
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
{
|
||||
"en": "Inglês",
|
||||
"af": "Africâner",
|
||||
"az": "Azerbaijanês",
|
||||
"bg": "Búlgaro",
|
||||
"cs": "Checo",
|
||||
|
||||
@@ -1,19 +1,11 @@
|
||||
{
|
||||
"en": "Tiếng Anh",
|
||||
"af": "Tiếng Afrika",
|
||||
"az": "Tiếng Azecbaizan",
|
||||
"bg": "Tiếng Bulgaria",
|
||||
"cs": "Tiếng Séc",
|
||||
"de": "Tiếng Đức",
|
||||
"el": "Tiếng Nhật",
|
||||
"eo": "Tiếng Esperanto",
|
||||
"es": "Tiếng Tây Ban Nha",
|
||||
"fr": "Tiếng Pháp",
|
||||
"hy": "Tiếng Acmenia",
|
||||
"it": "Tiếng Ý",
|
||||
"ja": "Tiếng Nhật",
|
||||
"ko": "Tiếng Hàn",
|
||||
"nb": "Tiếng Na Uy",
|
||||
"oc": "Tiếng Occitan",
|
||||
"pl": "Tiếng Ba Lan",
|
||||
"ptBR": "Tiếng Bồ Đào Nha (Brazil)",
|
||||
@@ -22,6 +14,7 @@
|
||||
"sl": "Tiếng Slovenia",
|
||||
"sv": "Tiếng Thụy Điển",
|
||||
"tr": "Tiếng Thổ Nhĩ Kỳ",
|
||||
"vi": "Tiếng Việt",
|
||||
"zhCN": "Tiếng Hoa (Trung Quốc)"
|
||||
"zhCN": "Tiếng Hoa (Trung Quốc)",
|
||||
"nb": "Tiếng Na Uy",
|
||||
"eo": "Tiếng Esperanto"
|
||||
}
|
||||
1100
lang/main-bg.json
1100
lang/main-bg.json
File diff suppressed because it is too large
Load Diff
@@ -35,43 +35,31 @@
|
||||
"raiseHand": "Hand erheben",
|
||||
"pushToTalk": "Drücken um zu sprechen",
|
||||
"toggleScreensharing": "Zwischen Kamera und Bildschirmfreigabe wechseln",
|
||||
"toggleFilmstrip": "",
|
||||
"toggleShortcuts": "",
|
||||
"toggleFilmstrip": "Videos anzeigen oder verbergen",
|
||||
"toggleShortcuts": "Hilfe-Menu anzeigen oder verdecken",
|
||||
"focusLocal": "Lokales Video fokussieren",
|
||||
"focusRemote": "Auf das Video eines anderen Teilnehmers fokussieren",
|
||||
"toggleChat": "Chat öffnen oder schliessen",
|
||||
"mute": "Stummschaltung aktivieren oder deaktivieren",
|
||||
"fullScreen": "Vollbildmodus aktivieren / deaktivieren",
|
||||
"videoMute": "Kamera starten oder stoppen",
|
||||
"showSpeakerStats": "Statistiken für Sprecher anzeigen",
|
||||
"localRecording": ""
|
||||
"showSpeakerStats": "Statistiken für Sprecher anzeigen"
|
||||
},
|
||||
"\u0005keyboardShortcuts": {},
|
||||
"welcomepage": {
|
||||
"accessibilityLabel": {
|
||||
"join": "",
|
||||
"roomname": "Konferenzname eingeben"
|
||||
},
|
||||
"appDescription": "Auf geht's! Beginne eine Videokonferenz mit dem ganzen Team. Oder eigentlich, lade alle ein die du kennst. __app__ ist eine vollständig verschlüsselte, aus 100% Open-Source-Software bestehende Videokonferenzlösung die du den ganzen Tag kostenlos verwenden kannst — ohne Registrierung.",
|
||||
"audioVideoSwitch": {
|
||||
"audio": "Sprache",
|
||||
"video": "Video"
|
||||
},
|
||||
"calendar": "Kalender",
|
||||
"connectCalendarText": "",
|
||||
"connectCalendarButton": "",
|
||||
"enterRoomTitle": "",
|
||||
"go": "Los",
|
||||
"join": "Beitreten",
|
||||
"privacy": "Privatsphäre",
|
||||
"recentList": "",
|
||||
"recentListDelete": "",
|
||||
"recentListEmpty": "",
|
||||
"roomname": "Konferenzname eingeben",
|
||||
"roomnameHint": "Name oder URL der Konferenz der Sie beitreten möchten. Sie können einen Namen erfinden, er muss nur den anderen Teilnehmern übermittelt werden damit sie der gleichen Konferenz beitreten.",
|
||||
"sendFeedback": "Senden Sie uns Ihr Feedback",
|
||||
"terms": "Bedingungen",
|
||||
"title": ""
|
||||
"title": "Sichere, flexible und vollständig freie Videokonferenzen"
|
||||
},
|
||||
"startupoverlay": {
|
||||
"policyText": "",
|
||||
@@ -83,41 +71,8 @@
|
||||
"rejoinKeyTitle": "Erneut teilnehmen"
|
||||
},
|
||||
"toolbar": {
|
||||
"accessibilityLabel": {
|
||||
"audioOnly": "",
|
||||
"audioRoute": "",
|
||||
"callQuality": "Qualitätseinstellungen",
|
||||
"chat": "",
|
||||
"cc": "",
|
||||
"document": "Geteiltes Dokument schliessen",
|
||||
"feedback": "Feedback hinterlasen",
|
||||
"fullScreen": "",
|
||||
"hangup": "",
|
||||
"invite": "Teilnehmer einladen",
|
||||
"localRecording": "",
|
||||
"lockRoom": "",
|
||||
"moreActions": "",
|
||||
"moreActionsMenu": "",
|
||||
"mute": "",
|
||||
"pip": "",
|
||||
"profile": "Profil bearbeiten",
|
||||
"raiseHand": "",
|
||||
"recording": "",
|
||||
"Settings": "",
|
||||
"sharedvideo": "",
|
||||
"shareRoom": "",
|
||||
"shareYourScreen": "",
|
||||
"shortcuts": "",
|
||||
"speakerStats": "",
|
||||
"toggleCamera": "",
|
||||
"tileView": "",
|
||||
"videomute": ""
|
||||
},
|
||||
"addPeople": "Teilnehmer zur Konferenz hinzufügen",
|
||||
"audioonly": "Nur-Audio-Modus aktivieren/deaktivieren (spart Bandbreite)",
|
||||
"audioOnlyOn": "Nur-Audio-Modus aktivieren/deaktivieren (spart Bandbreite)",
|
||||
"audioOnlyOff": "",
|
||||
"audioRoute": "",
|
||||
"callQuality": "Qualitätseinstellungen",
|
||||
"enterFullScreen": "Vollbildmodus",
|
||||
"exitFullScreen": "Vollbildmodus verlassen",
|
||||
@@ -131,8 +86,8 @@
|
||||
"etherpad": "Geteiltes Dokument öffnen / schliessen",
|
||||
"documentOpen": "Geteiltes Dokument öffnen",
|
||||
"documentClose": "Geteiltes Dokument schliessen",
|
||||
"shareRoom": "",
|
||||
"sharedvideo": "YouTube-Video teilen",
|
||||
"sharescreen": "Bildschirmfreigabe",
|
||||
"stopSharedVideo": "YouTube Video stoppen",
|
||||
"fullscreen": "Vollbildmodus aktivieren / deaktivieren",
|
||||
"sip": "SIP Nummer anrufen",
|
||||
@@ -141,40 +96,25 @@
|
||||
"login": "Anmelden",
|
||||
"logout": "Abmelden",
|
||||
"sharedVideoMutedPopup": "Das freigegebene Video wurde stumm geschaltet um mit den anderen Teilnehmern zu sprechen.",
|
||||
"toggleCamera": "",
|
||||
"micMutedPopup": "Das Mikrofon wurde stumm geschaltet um das freigegebene Video geniessen zu können.",
|
||||
"talkWhileMutedPopup": "Versuchen sie zu sprechen? Ihr Mikrofon ist stummgeschaltet.",
|
||||
"unableToUnmutePopup": "Die Stummschaltung kann nicht aufgehoben werden während das geteilte Video abgespielt wird.",
|
||||
"cameraDisabled": "Keine Kamera verfügbar",
|
||||
"micDisabled": "Kein Mikrofon verfügbar",
|
||||
"filmstrip": "Videos anzeigen / verbergen",
|
||||
"pip": "",
|
||||
"profile": "Profil bearbeiten",
|
||||
"raiseHand": "Hand erheben",
|
||||
"shortcuts": "Tastenkürzel anzeigen",
|
||||
"speakerStats": "Sprecher-Statistiken",
|
||||
"tileViewToggle": "",
|
||||
"invite": "Teilnehmer einladen"
|
||||
},
|
||||
"\u0005toolbar": {
|
||||
"accessibilityLabel": {}
|
||||
"speakerStats": "Sprecher-Statistiken"
|
||||
},
|
||||
"chat": {
|
||||
"nickname": {
|
||||
"title": "Name eingeben",
|
||||
"popover": "Name"
|
||||
},
|
||||
"error": "",
|
||||
"messagebox": "Text eingeben..."
|
||||
},
|
||||
"settings": {
|
||||
"calendar": {
|
||||
"about": "",
|
||||
"disconnect": "Getrennt",
|
||||
"microsoftSignIn": "",
|
||||
"signedIn": "",
|
||||
"title": "Kalender"
|
||||
},
|
||||
"title": "Einstellungen",
|
||||
"update": "Aktualisieren",
|
||||
"name": "Name",
|
||||
@@ -184,18 +124,11 @@
|
||||
"selectMic": "Mikrofon",
|
||||
"selectAudioOutput": "Audioausgabe",
|
||||
"followMe": "Follow-me für alle Teilnehmer",
|
||||
"language": "",
|
||||
"loggedIn": "",
|
||||
"noDevice": "Kein",
|
||||
"cameraAndMic": "Kamera und Mikrofon",
|
||||
"moderator": "Moderator",
|
||||
"more": "",
|
||||
"moderator": "MODERATOR",
|
||||
"password": "PASSWORT SETZEN",
|
||||
"audioVideo": "AUDIO UND VIDEO",
|
||||
"devices": ""
|
||||
},
|
||||
"\u0005settings": {
|
||||
"calendar": {}
|
||||
"audioVideo": "AUDIO UND VIDEO"
|
||||
},
|
||||
"profile": {
|
||||
"title": "Profil",
|
||||
@@ -215,9 +148,7 @@
|
||||
},
|
||||
"connectionindicator": {
|
||||
"header": "Verbindungsdaten",
|
||||
"connectedTo": "",
|
||||
"bitrate": "Bitrate:",
|
||||
"bridgeCount": "",
|
||||
"packetloss": "Paketverlust:",
|
||||
"resolution": "Auflösung:",
|
||||
"framerate": "Bildwiederholrate:",
|
||||
@@ -256,6 +187,7 @@
|
||||
"focus": "Konferenz-Organisator",
|
||||
"focusFail": "__component__ ist im Moment nicht verfügbar - wiederholen in __ms__ Sekunden",
|
||||
"grantedTo": "Moderatorenrechte an __to__ vergeben.",
|
||||
"grantedToUnknown": "Moderatorenrechte an $t(notify.somebody) vergeben.",
|
||||
"muted": "Der Konferenz wurde stumm beigetreten.",
|
||||
"mutedTitle": "Stummschaltung aktiv!",
|
||||
"raisedHand": "Möchte sprechen.",
|
||||
@@ -263,13 +195,8 @@
|
||||
"suboptimalExperienceDescription": "Tut uns leid, aber die Konferenz wird mit __appName__ kein grossartiges Erlebnis. Wir versuchen immer die Situation zu verbessern, bis dahin empfehlen wir aber die Verwendung einer der <a href=\"static/recommendedBrowsers.html\" target=\"_blank\">vollständig unterstützen Browser</a>."
|
||||
},
|
||||
"dialog": {
|
||||
"accessibilityLabel": {
|
||||
"liveStreaming": "Livestream:"
|
||||
},
|
||||
"allow": "Erlauben",
|
||||
"confirm": "",
|
||||
"kickMessage": "Oh! Sie wurden aus der Konferenz ausgeschlossen.",
|
||||
"kickTitle": "",
|
||||
"popupErrorTitle": "Popup blockiert",
|
||||
"popupError": "Ihr Browser blockiert Popups von dieser Website. Bitte aktivieren Sie Popups in den Sicherheitseinstellungen des Browsers und versuchen Sie es erneut.",
|
||||
"passwordErrorTitle": "Passwort-Fehler",
|
||||
@@ -292,7 +219,6 @@
|
||||
"rejoinNow": "Jetzt erneut beitreten",
|
||||
"maxUsersLimitReachedTitle": "Maximale Anzahl Teilnehmer ist erreicht",
|
||||
"maxUsersLimitReached": "Das Limit der maximalen Anzahl Teilnehmer wurde erreicht. Die Konferenz ist voll. Bitte benachrichtigen Sie den Konferenzorganisator oder versuchen Sie es später noch einmal.",
|
||||
"lockRoom": "",
|
||||
"lockTitle": "Sperren fehlgeschlagen",
|
||||
"lockMessage": "Die Konferenz konnte nicht gesperrt werden.",
|
||||
"warning": "Warnung",
|
||||
@@ -340,7 +266,6 @@
|
||||
"reservationError": "Fehler im Reservationssystem",
|
||||
"reservationErrorMsg": "Fehler, Nummer: __code__, Nachricht: __msg__",
|
||||
"password": "Passwort eingeben",
|
||||
"unlockRoom": "",
|
||||
"userPassword": "Benutzerpasswort",
|
||||
"token": "Token",
|
||||
"tokenAuthFailedTitle": "Authentifizierung fehlgeschlagen",
|
||||
@@ -353,7 +278,7 @@
|
||||
"sorryFeedback": "Tut uns leid. Möchten Sie uns mehr mitteilen?",
|
||||
"liveStreaming": "Live-Streaming",
|
||||
"streamKey": "Name/Schlüssel für den Stream",
|
||||
"startLiveStreaming": "Einen Livestream starten",
|
||||
"startLiveStreaming": "Live-Streaming starten",
|
||||
"startRecording": "Aufnahme starten",
|
||||
"stopStreamingWarning": "Sind Sie sicher dass Sie das Live-Streaming stoppen möchten?",
|
||||
"stopRecordingWarning": "Sind Sie sicher dass Sie die Aufnahme stoppen möchten?",
|
||||
@@ -387,10 +312,6 @@
|
||||
"muteParticipantTitle": "Diesen Teilnehmer stummschalten?",
|
||||
"muteParticipantBody": "Sie können die Stummschaltung anderer Teilnehmer nicht aufheben, aber ein Teilnehmer kann seine eigene Stummschaltung jederzeit beenden.",
|
||||
"muteParticipantButton": "Stummschalten",
|
||||
"liveStreamingDisabledTooltip": "",
|
||||
"liveStreamingDisabledForGuestTooltip": "",
|
||||
"recordingDisabledTooltip": "",
|
||||
"recordingDisabledForGuestTooltip": "",
|
||||
"remoteControlTitle": "Fernsteuerung",
|
||||
"remoteControlRequestMessage": "Möchten Sie __user__ erlauben den Computer fernzusteuern?",
|
||||
"remoteControlShareScreenWarning": "Achtung, wenn Sie die Anfrage genehmigen starten Sie die Bildschirmfreigabe!",
|
||||
@@ -401,15 +322,10 @@
|
||||
"remoteControlStopMessage": "Die Fernsteuerung wurde beendet.",
|
||||
"close": "Schliessen",
|
||||
"shareYourScreen": "Bildschirm freigeben",
|
||||
"shareYourScreenDisabled": "",
|
||||
"shareYourScreenDisabledForGuest": "",
|
||||
"yourEntireScreen": "Ganzer Bildschirm",
|
||||
"applicationWindow": "Anwendungsfenster",
|
||||
"transcribing": ""
|
||||
},
|
||||
"\u0005dialog": {
|
||||
"accessibilityLabel": {}
|
||||
"applicationWindow": "Anwendungsfenster"
|
||||
},
|
||||
"\u0005dialog": {},
|
||||
"email": {
|
||||
"sharedKey": [
|
||||
"Diese Konferenz ist passwortgeschützt. Bitte verwenden Sie diese PIN zum Beitreten:",
|
||||
@@ -441,10 +357,6 @@
|
||||
],
|
||||
"and": "und"
|
||||
},
|
||||
"share": {
|
||||
"mainText": "",
|
||||
"dialInfoText": ""
|
||||
},
|
||||
"connection": {
|
||||
"ERROR": "Fehler",
|
||||
"CONNECTING": "Verbindung wird hergestellt",
|
||||
@@ -458,44 +370,18 @@
|
||||
"ATTACHED": "Angehängt"
|
||||
},
|
||||
"recording": {
|
||||
"beta": "",
|
||||
"busy": "Es werden Resourcen für eine Aufnahme bereitgestellt. Bitte in ein paar Minuten erneut versuchen.",
|
||||
"busyTitle": "Alle Aufnahme-Instanzen sind in Gebrauch",
|
||||
"buttonTooltip": "Aufnahme starten / stoppen",
|
||||
"error": "Die Aufzeichnung ist fehlgeschlagen. Bitte versuchen Sie es erneut.",
|
||||
"expandedOff": "",
|
||||
"expandedOn": "",
|
||||
"expandedPending": "",
|
||||
"failedToStart": "Die Aufnahme konnte nicht gestartet werden",
|
||||
"live": "",
|
||||
"off": "Aufnahme gestoppt",
|
||||
"on": "Aufnahme",
|
||||
"pending": "",
|
||||
"rec": "",
|
||||
"authDropboxText": "",
|
||||
"pending": "Die Aufnahme wartet auf den Beitritt eines Teilnehmers...",
|
||||
"serviceName": "Aufnahmedienst",
|
||||
"signOut": "",
|
||||
"signIn": "",
|
||||
"loggedIn": "",
|
||||
"availableSpace": "",
|
||||
"startRecordingBody": "Sind Sie sicher dass Sie die Aufnahme stoppen möchten?",
|
||||
"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"
|
||||
},
|
||||
"\u0005recording": {},
|
||||
"transcribing": {
|
||||
"pending": "",
|
||||
"off": "",
|
||||
"error": "Die Aufzeichnung ist fehlgeschlagen. Bitte versuchen Sie es erneut.",
|
||||
"expandedLabel": "",
|
||||
"failedToStart": "",
|
||||
"tr": "",
|
||||
"labelToolTip": "",
|
||||
"ccButtonTooltip": "",
|
||||
"start": "",
|
||||
"stop": ""
|
||||
},
|
||||
"\u0005transcribing": {},
|
||||
"liveStreaming": {
|
||||
"busy": "Es werden Resourcen zum Streamen bereitgestellt. Bitte in ein paar Minuten erneut versuchen.",
|
||||
"busyTitle": "Alle Streaming-Instanzen sind in Gebrauch",
|
||||
@@ -506,24 +392,17 @@
|
||||
"enterStreamKey": "Name/Schlüssel für den YouTube Livestream hier eingeben.",
|
||||
"error": "Das Live-Streaming ist fehlgeschlagen. Bitte versuchen Sie es erneut.",
|
||||
"errorAPI": "Beim abrufen der YouTube Livestreams ist ein Fehler aufgetreten. Bitte versuchen Sie sich erneut anzumelden.",
|
||||
"errorLiveStreamNotEnabled": "",
|
||||
"expandedOff": "",
|
||||
"expandedOn": "",
|
||||
"expandedPending": "",
|
||||
"failedToStart": "Live-Streaming konnte nicht gestartet werden",
|
||||
"off": "Live-Streaming gestoppt",
|
||||
"on": "Live-Streaming",
|
||||
"pending": "Live-Stream wird gestartet...",
|
||||
"serviceName": "Live Streaming-Dienst",
|
||||
"signedInAs": "",
|
||||
"signIn": "Mit Google anmelden",
|
||||
"signOut": "",
|
||||
"signInCTA": "Anmelden oder den Name/Schlüssel des YouTube Livestreams eingeben.",
|
||||
"start": "Einen Livestream starten",
|
||||
"streamIdHelp": "Was ist das?",
|
||||
"unavailableTitle": "Live-Streaming nicht verfügbar"
|
||||
},
|
||||
"\u0005liveStreaming": {},
|
||||
"videoSIPGW": {
|
||||
"busy": "Es stehen keine freien Ressourcen zur Verfügung. Bitte versuchen Sie es später noch einmal.",
|
||||
"busyTitle": "Keine freien Ressourcen",
|
||||
@@ -549,11 +428,9 @@
|
||||
"noPermission": "Berechtigungen nicht erteilt",
|
||||
"previewUnavailable": "Keine Vorschau verfügbar",
|
||||
"selectADevice": "Ein Gerät wählen",
|
||||
"testAudio": ""
|
||||
"testAudio": "Audio testen"
|
||||
},
|
||||
"videoStatus": {
|
||||
"audioOnly": "",
|
||||
"audioOnlyExpanded": "",
|
||||
"callQuality": "Konferenzqualität",
|
||||
"hd": "HD",
|
||||
"hdTooltip": "Video wird in HD angezeigt",
|
||||
@@ -581,8 +458,7 @@
|
||||
"add": "Einladen",
|
||||
"countryNotSupported": "Wir unterstützen dieses Land noch nicht.",
|
||||
"countryReminder": "Telefonnummer nicht in den USA? Bitte sicherstellen, dass die Telefonnummer mit dem Ländercode beginnt.",
|
||||
"disabled": "Sie können keine Teilnehmer einladen.",
|
||||
"footerText": "",
|
||||
"disabled": "",
|
||||
"invite": "Einladen",
|
||||
"loading": "Suche nach Teilnehmern und Telefonnummern",
|
||||
"loadingNumber": "Telefonnummer wird überprüft",
|
||||
@@ -619,7 +495,6 @@
|
||||
"veryGood": "Sehr gut"
|
||||
},
|
||||
"info": {
|
||||
"accessibilityLabel": "",
|
||||
"addPassword": "Passwort hinzufügen",
|
||||
"cancelPassword": "Password abbrechen",
|
||||
"conferenceURL": "Link:",
|
||||
@@ -641,7 +516,7 @@
|
||||
"numbers": "Einwählnummern",
|
||||
"password": "Passwort:",
|
||||
"title": "Teilen",
|
||||
"tooltip": ""
|
||||
"tooltip": "Zugriffsinformationen über die Konferenz abrufen"
|
||||
},
|
||||
"settingsView": {
|
||||
"alertOk": "OK",
|
||||
@@ -657,22 +532,17 @@
|
||||
"startWithVideoMuted": "Ohne Video beitreten"
|
||||
},
|
||||
"calendarSync": {
|
||||
"addMeetingURL": "",
|
||||
"confirmAddLink": "",
|
||||
"confirmAddLinkTitle": "Kalender",
|
||||
"join": "",
|
||||
"joinTooltip": "",
|
||||
"later": "Später",
|
||||
"next": "Folgend",
|
||||
"nextMeeting": "Nächste Konferenz",
|
||||
"noEvents": "",
|
||||
"ongoingMeeting": "",
|
||||
"now": "Jetzt",
|
||||
"permissionButton": "Einstellungen öffnen",
|
||||
"permissionMessage": "Die App benötigt Zugriff auf den Kalender um die Termine und Konferenzen anzuzeigen.",
|
||||
"refresh": "",
|
||||
"today": "Heute"
|
||||
"permissionMessage": "Die App benötigt Zugriff auf den Kalender um die Termine und Konferenzen anzuzeigen."
|
||||
},
|
||||
"\u0005calendarSync": {},
|
||||
"recentList": {
|
||||
"joinPastMeeting": ""
|
||||
"today": "Heute",
|
||||
"yesterday": "Gestern",
|
||||
"earlier": "Früher"
|
||||
},
|
||||
"sectionList": {
|
||||
"pullToRefresh": "Ziehen um zu aktualisieren"
|
||||
@@ -685,62 +555,5 @@
|
||||
"appNotInstalled": "Sie benötigen die __app__ App um der Konferenz auf dem Smartphone beizutreten.",
|
||||
"downloadApp": "App herunterladen",
|
||||
"openApp": "In der App fortfahren"
|
||||
},
|
||||
"presenceStatus": {
|
||||
"invited": "Einladen",
|
||||
"ringing": "",
|
||||
"calling": "",
|
||||
"initializingCall": "",
|
||||
"connected": "Verbunden",
|
||||
"connecting": "Verbindung wird hergestellt",
|
||||
"connecting2": "",
|
||||
"disconnected": "Getrennt",
|
||||
"busy": "",
|
||||
"rejected": "",
|
||||
"ignored": "",
|
||||
"expired": ""
|
||||
},
|
||||
"\u0005presenceStatus": {},
|
||||
"dateUtils": {
|
||||
"today": "Heute",
|
||||
"yesterday": "Gestern",
|
||||
"earlier": "Früher"
|
||||
},
|
||||
"incomingCall": {
|
||||
"answer": "",
|
||||
"audioCallTitle": "",
|
||||
"decline": "OK",
|
||||
"productLabel": "",
|
||||
"videoCallTitle": ""
|
||||
},
|
||||
"localRecording": {
|
||||
"localRecording": "",
|
||||
"dialogTitle": "",
|
||||
"start": "Aufnahme starten",
|
||||
"stop": "Aufnahme stoppen",
|
||||
"moderator": "Moderator",
|
||||
"me": "Ich",
|
||||
"duration": "",
|
||||
"durationNA": "",
|
||||
"encoding": "",
|
||||
"participantStats": "",
|
||||
"participant": "Teilnehmer",
|
||||
"sessionToken": "",
|
||||
"clientState": {
|
||||
"on": "",
|
||||
"off": "",
|
||||
"unknown": ""
|
||||
},
|
||||
"messages": {
|
||||
"engaged": "",
|
||||
"finished": "",
|
||||
"finishedModerator": "",
|
||||
"notModerator": ""
|
||||
},
|
||||
"yes": "Ja",
|
||||
"no": "",
|
||||
"label": "",
|
||||
"labelToolTip": ""
|
||||
},
|
||||
"\u0005localRecording": {}
|
||||
}
|
||||
}
|
||||
@@ -1,746 +0,0 @@
|
||||
{
|
||||
"addPeople": {
|
||||
"add": "",
|
||||
"countryNotSupported": "",
|
||||
"countryReminder": "",
|
||||
"disabled": "",
|
||||
"failedToAdd": "",
|
||||
"footerText": "",
|
||||
"invite": "",
|
||||
"loading": "",
|
||||
"loadingNumber": "",
|
||||
"loadingPeople": "",
|
||||
"noResults": "",
|
||||
"notAvailable": "",
|
||||
"noValidNumbers": "",
|
||||
"searchNumbers": "",
|
||||
"searchPeople": "",
|
||||
"searchPeopleAndNumbers": "",
|
||||
"telephone": "",
|
||||
"title": ""
|
||||
},
|
||||
"audioDevices": {
|
||||
"bluetooth": "",
|
||||
"headphones": "",
|
||||
"phone": "",
|
||||
"speaker": ""
|
||||
},
|
||||
"audioOnly": {
|
||||
"audioOnly": "",
|
||||
"featureToggleDisabled": ""
|
||||
},
|
||||
"calendarSync": {
|
||||
"addMeetingURL": "",
|
||||
"confirmAddLink": "",
|
||||
"confirmAddLinkTitle": "",
|
||||
"error": {
|
||||
"appConfiguration": "",
|
||||
"generic": "",
|
||||
"notSignedIn": ""
|
||||
},
|
||||
"join": "",
|
||||
"joinTooltip": "",
|
||||
"nextMeeting": "",
|
||||
"noEvents": "",
|
||||
"ongoingMeeting": "",
|
||||
"permissionButton": "",
|
||||
"permissionMessage": "",
|
||||
"refresh": "",
|
||||
"today": ""
|
||||
},
|
||||
"chat": {
|
||||
"error": "",
|
||||
"messagebox": "",
|
||||
"nickname": {
|
||||
"popover": "",
|
||||
"title": ""
|
||||
},
|
||||
"title": ""
|
||||
},
|
||||
"connection": {
|
||||
"ATTACHED": "",
|
||||
"AUTHENTICATING": "",
|
||||
"AUTHFAIL": "",
|
||||
"CONNECTED": "",
|
||||
"CONNECTING": "",
|
||||
"CONNFAIL": "",
|
||||
"DISCONNECTED": "",
|
||||
"DISCONNECTING": "",
|
||||
"ERROR": "",
|
||||
"RECONNECTING": ""
|
||||
},
|
||||
"connectionindicator": {
|
||||
"address": "",
|
||||
"bandwidth": "",
|
||||
"bitrate": "",
|
||||
"bridgeCount": "",
|
||||
"connectedTo": "",
|
||||
"framerate": "",
|
||||
"header": "",
|
||||
"less": "",
|
||||
"localaddress": "",
|
||||
"localaddress_plural_2": "",
|
||||
"localport": "",
|
||||
"localport_plural_2": "",
|
||||
"more": "",
|
||||
"na": "",
|
||||
"packetloss": "",
|
||||
"quality": {
|
||||
"good": "",
|
||||
"inactive": "",
|
||||
"lost": "",
|
||||
"nonoptimal": "",
|
||||
"poor": ""
|
||||
},
|
||||
"remoteaddress": "",
|
||||
"remoteaddress_plural_2": "",
|
||||
"remoteport": "",
|
||||
"remoteport_plural_2": "",
|
||||
"resolution": "",
|
||||
"status": "",
|
||||
"transport": "",
|
||||
"transport_plural_2": "",
|
||||
"turn": ""
|
||||
},
|
||||
"contactlist_plural": "",
|
||||
"dateUtils": {
|
||||
"earlier": "",
|
||||
"today": "",
|
||||
"yesterday": ""
|
||||
},
|
||||
"deepLinking": {
|
||||
"appNotInstalled": "",
|
||||
"description": "",
|
||||
"downloadApp": "",
|
||||
"launchWebButton": "",
|
||||
"openApp": "",
|
||||
"title": "",
|
||||
"tryAgainButton": ""
|
||||
},
|
||||
"defaultLink": "",
|
||||
"defaultNickname": "",
|
||||
"deviceError": {
|
||||
"cameraError": "",
|
||||
"cameraPermission": "",
|
||||
"microphoneError": "",
|
||||
"microphonePermission": ""
|
||||
},
|
||||
"deviceSelection": {
|
||||
"deviceSettings": "",
|
||||
"noPermission": "",
|
||||
"previewUnavailable": "",
|
||||
"selectADevice": "",
|
||||
"testAudio": ""
|
||||
},
|
||||
"dialog": {
|
||||
"accessibilityLabel": {
|
||||
"liveStreaming": ""
|
||||
},
|
||||
"allow": "",
|
||||
"alreadySharedVideoMsg": "",
|
||||
"alreadySharedVideoTitle": "",
|
||||
"applicationWindow": "",
|
||||
"Back": "",
|
||||
"cameraConstraintFailedError": "",
|
||||
"cameraNotFoundError": "",
|
||||
"cameraNotSendingData": "",
|
||||
"cameraNotSendingDataTitle": "",
|
||||
"cameraPermissionDeniedError": "",
|
||||
"cameraUnknownError": "",
|
||||
"cameraUnsupportedResolutionError": "",
|
||||
"Cancel": "",
|
||||
"close": "",
|
||||
"conferenceDisconnectMsg": "",
|
||||
"conferenceDisconnectTitle": "",
|
||||
"conferenceReloadMsg": "",
|
||||
"conferenceReloadTitle": "",
|
||||
"confirm": "",
|
||||
"confirmNo": "",
|
||||
"confirmYes": "",
|
||||
"connectError": "",
|
||||
"connectErrorWithMsg": "",
|
||||
"connecting": "",
|
||||
"contactSupport": "",
|
||||
"copy": "",
|
||||
"currentPassword": "",
|
||||
"defaultError": "",
|
||||
"detectext": "",
|
||||
"dismiss": "",
|
||||
"displayNameRequired": "",
|
||||
"done": "",
|
||||
"doNotShowMessageAgain": "",
|
||||
"enterDisplayName": "",
|
||||
"error": "",
|
||||
"externalInstallationMsg": "",
|
||||
"externalInstallationTitle": "",
|
||||
"failedpermissions": "",
|
||||
"feedbackHelp": "",
|
||||
"feedbackQuestion": "",
|
||||
"goToStore": "",
|
||||
"gracefulShutdown": "",
|
||||
"hungUp": "",
|
||||
"IamHost": "",
|
||||
"incorrectPassword": "",
|
||||
"inlineInstallationMsg": "",
|
||||
"inlineInstallExtension": "",
|
||||
"internalError": "",
|
||||
"internalErrorTitle": "",
|
||||
"joinAgain": "",
|
||||
"kickMessage": "",
|
||||
"kickParticipantButton": "",
|
||||
"kickParticipantDialog": "",
|
||||
"kickParticipantTitle": "",
|
||||
"kickTitle": "",
|
||||
"liveStreaming": "",
|
||||
"liveStreamingDisabledForGuestTooltip": "",
|
||||
"liveStreamingDisabledTooltip": "",
|
||||
"lockMessage": "",
|
||||
"lockRoom": "",
|
||||
"lockTitle": "",
|
||||
"logoutQuestion": "",
|
||||
"logoutTitle": "",
|
||||
"maxUsersLimitReached": "",
|
||||
"maxUsersLimitReachedTitle": "",
|
||||
"micConstraintFailedError": "",
|
||||
"micNotFoundError": "",
|
||||
"micNotSendingData": "",
|
||||
"micNotSendingDataTitle": "",
|
||||
"micPermissionDeniedError": "",
|
||||
"micUnknownError": "",
|
||||
"muteParticipantBody": "",
|
||||
"muteParticipantButton": "",
|
||||
"muteParticipantDialog": "",
|
||||
"muteParticipantTitle": "",
|
||||
"Ok": "",
|
||||
"oops": "",
|
||||
"password": "",
|
||||
"passwordError": "",
|
||||
"passwordError2": "",
|
||||
"passwordErrorTitle": "",
|
||||
"passwordLabel": "",
|
||||
"passwordNotSupported": "",
|
||||
"passwordNotSupportedTitle": "",
|
||||
"passwordRequired": "",
|
||||
"permissionDenied": "",
|
||||
"popupError": "",
|
||||
"popupErrorTitle": "",
|
||||
"recording": "",
|
||||
"recordingDisabledForGuestTooltip": "",
|
||||
"recordingDisabledTooltip": "",
|
||||
"recordingToken": "",
|
||||
"rejoinNow": "",
|
||||
"remoteControlAllowedMessage": "",
|
||||
"remoteControlDeniedMessage": "",
|
||||
"remoteControlErrorMessage": "",
|
||||
"remoteControlRequestMessage": "",
|
||||
"remoteControlShareScreenWarning": "",
|
||||
"remoteControlStopMessage": "",
|
||||
"remoteControlTitle": "",
|
||||
"Remove": "",
|
||||
"removePassword": "",
|
||||
"removeSharedVideoMsg": "",
|
||||
"removeSharedVideoTitle": "",
|
||||
"reservationError": "",
|
||||
"reservationErrorMsg": "",
|
||||
"retry": "",
|
||||
"Save": "",
|
||||
"screenSharingFailedToInstall": "",
|
||||
"screenSharingFailedToInstallTitle": "",
|
||||
"screenSharingFirefoxPermissionDeniedError": "",
|
||||
"screenSharingFirefoxPermissionDeniedTitle": "",
|
||||
"screenSharingPermissionDeniedError": "",
|
||||
"serviceUnavailable": "",
|
||||
"sessTerminated": "",
|
||||
"Share": "",
|
||||
"shareVideoLinkError": "",
|
||||
"shareVideoTitle": "",
|
||||
"shareYourScreen": "",
|
||||
"shareYourScreenDisabled": "",
|
||||
"shareYourScreenDisabledForGuest": "",
|
||||
"SLDFailure": "",
|
||||
"sorryFeedback": "",
|
||||
"SRDFailure": "",
|
||||
"startLiveStreaming": "",
|
||||
"startRecording": "",
|
||||
"startRemoteControlErrorMessage": "",
|
||||
"stopLiveStreaming": "",
|
||||
"stopRecording": "",
|
||||
"stopRecordingWarning": "",
|
||||
"stopStreamingWarning": "",
|
||||
"streamKey": "",
|
||||
"Submit": "",
|
||||
"thankYou": "",
|
||||
"token": "",
|
||||
"tokenAuthFailed": "",
|
||||
"tokenAuthFailedTitle": "",
|
||||
"transcribing": "",
|
||||
"unableToSwitch": "",
|
||||
"unlockRoom": "",
|
||||
"userPassword": "",
|
||||
"WaitForHostMsg": "",
|
||||
"WaitForHostMsgWOk": "",
|
||||
"WaitingForHost": "",
|
||||
"warning": "",
|
||||
"Yes": "",
|
||||
"yourEntireScreen": ""
|
||||
},
|
||||
"dialOut": {
|
||||
"statusMessage": ""
|
||||
},
|
||||
"email": {
|
||||
"and": "",
|
||||
"body": "",
|
||||
"sharedKey": "",
|
||||
"subject": ""
|
||||
},
|
||||
"feedback": {
|
||||
"average": "",
|
||||
"bad": "",
|
||||
"detailsLabel": "",
|
||||
"good": "",
|
||||
"rateExperience": "",
|
||||
"veryBad": "",
|
||||
"veryGood": ""
|
||||
},
|
||||
"incomingCall": {
|
||||
"answer": "",
|
||||
"audioCallTitle": "",
|
||||
"decline": "",
|
||||
"productLabel": "",
|
||||
"videoCallTitle": ""
|
||||
},
|
||||
"info": {
|
||||
"accessibilityLabel": "",
|
||||
"addPassword": "",
|
||||
"cancelPassword": "",
|
||||
"conferenceURL": "",
|
||||
"country": "",
|
||||
"dialANumber": "",
|
||||
"dialInConferenceID": "",
|
||||
"dialInNotSupported": "",
|
||||
"dialInNumber": "",
|
||||
"dialInTollFree": "",
|
||||
"genericError": "",
|
||||
"inviteLiveStream": "",
|
||||
"invitePhone": "",
|
||||
"invitePhoneAlternatives": "",
|
||||
"inviteURL": "",
|
||||
"liveStreamURL": "",
|
||||
"moreNumbers": "",
|
||||
"noNumbers": "",
|
||||
"noPassword": "",
|
||||
"noRoom": "",
|
||||
"numbers": "",
|
||||
"password": "",
|
||||
"title": "",
|
||||
"tooltip": "",
|
||||
"label": ""
|
||||
},
|
||||
"inviteDialog": {
|
||||
"alertOk": "",
|
||||
"alertText": "",
|
||||
"alertTitle": "",
|
||||
"header": "",
|
||||
"searchCallOnlyPlaceholder": "",
|
||||
"searchPeopleOnlyPlaceholder": "",
|
||||
"searchPlaceholder": "",
|
||||
"send": ""
|
||||
},
|
||||
"inlineDialogFailure": {
|
||||
"msg": "",
|
||||
"retry": "",
|
||||
"support": "",
|
||||
"supportMsg": ""
|
||||
},
|
||||
"inviteUrlDefaultMsg": "",
|
||||
"keyboardShortcuts": {
|
||||
"focusLocal": "",
|
||||
"focusRemote": "",
|
||||
"fullScreen": "",
|
||||
"keyboardShortcuts": "",
|
||||
"localRecording": "",
|
||||
"mute": "",
|
||||
"pushToTalk": "",
|
||||
"raiseHand": "",
|
||||
"showSpeakerStats": "",
|
||||
"toggleChat": "",
|
||||
"toggleFilmstrip": "",
|
||||
"toggleScreensharing": "",
|
||||
"toggleShortcuts": "",
|
||||
"videoMute": ""
|
||||
},
|
||||
"liveStreaming": {
|
||||
"busy": "",
|
||||
"busyTitle": "",
|
||||
"buttonTooltip": "",
|
||||
"changeSignIn": "",
|
||||
"choose": "",
|
||||
"chooseCTA": "",
|
||||
"enterStreamKey": "",
|
||||
"error": "",
|
||||
"errorAPI": "",
|
||||
"errorLiveStreamNotEnabled": "",
|
||||
"expandedOff": "",
|
||||
"expandedOn": "",
|
||||
"expandedPending": "",
|
||||
"failedToStart": "",
|
||||
"getStreamKeyManually": "",
|
||||
"invalidStreamKey": "",
|
||||
"off": "",
|
||||
"on": "",
|
||||
"pending": "",
|
||||
"serviceName": "",
|
||||
"signedInAs": "",
|
||||
"signIn": "",
|
||||
"signInCTA": "",
|
||||
"signOut": "",
|
||||
"start": "",
|
||||
"streamIdHelp": "",
|
||||
"unavailableTitle": ""
|
||||
},
|
||||
"localRecording": {
|
||||
"clientState": {
|
||||
"off": "",
|
||||
"on": "",
|
||||
"unknown": ""
|
||||
},
|
||||
"dialogTitle": "",
|
||||
"duration": "",
|
||||
"durationNA": "",
|
||||
"encoding": "",
|
||||
"label": "",
|
||||
"labelToolTip": "",
|
||||
"localRecording": "",
|
||||
"me": "",
|
||||
"messages": {
|
||||
"engaged": "",
|
||||
"finished": "",
|
||||
"finishedModerator": "",
|
||||
"notModerator": ""
|
||||
},
|
||||
"moderator": "",
|
||||
"no": "",
|
||||
"participant": "",
|
||||
"participantStats": "",
|
||||
"sessionToken": "",
|
||||
"start": "",
|
||||
"stop": "",
|
||||
"yes": ""
|
||||
},
|
||||
"me": "",
|
||||
"notify": {
|
||||
"connectedOneMember": "",
|
||||
"connectedThreePlusMembers": "",
|
||||
"connectedTwoMembers": "",
|
||||
"disconnected": "",
|
||||
"focus": "",
|
||||
"focusFail": "",
|
||||
"grantedTo": "",
|
||||
"me": "",
|
||||
"moderator": "",
|
||||
"muted": "",
|
||||
"mutedTitle": "",
|
||||
"raisedHand": "",
|
||||
"somebody": "",
|
||||
"suboptimalExperienceDescription": "",
|
||||
"suboptimalExperienceTitle": ""
|
||||
},
|
||||
"passwordSetRemotely": "",
|
||||
"poweredby": "",
|
||||
"presenceStatus": {
|
||||
"busy": "",
|
||||
"calling": "",
|
||||
"connected": "",
|
||||
"connecting": "",
|
||||
"connecting2": "",
|
||||
"disconnected": "",
|
||||
"expired": "",
|
||||
"ignored": "",
|
||||
"initializingCall": "",
|
||||
"invited": "",
|
||||
"rejected": "",
|
||||
"ringing": ""
|
||||
},
|
||||
"profile": {
|
||||
"setDisplayNameLabel": "",
|
||||
"setEmailInput": "",
|
||||
"setEmailLabel": "",
|
||||
"title": ""
|
||||
},
|
||||
"raisedHand": "",
|
||||
"recentList": {
|
||||
"joinPastMeeting": ""
|
||||
},
|
||||
"recording": {
|
||||
"authDropboxText": "",
|
||||
"availableSpace": "",
|
||||
"beta": "",
|
||||
"busy": "",
|
||||
"busyTitle": "",
|
||||
"buttonTooltip": "",
|
||||
"error": "",
|
||||
"expandedOff": "",
|
||||
"expandedOn": "",
|
||||
"expandedPending": "",
|
||||
"failedToStart": "",
|
||||
"live": "",
|
||||
"loggedIn": "",
|
||||
"off": "",
|
||||
"on": "",
|
||||
"pending": "",
|
||||
"rec": "",
|
||||
"serviceDescription": "",
|
||||
"serviceName": "",
|
||||
"signIn": "",
|
||||
"signOut": "",
|
||||
"startRecordingBody": "",
|
||||
"unavailable": "",
|
||||
"unavailableTitle": ""
|
||||
},
|
||||
"sectionList": {
|
||||
"pullToRefresh": ""
|
||||
},
|
||||
"settings": {
|
||||
"audioVideo": "",
|
||||
"calendar": {
|
||||
"about": "",
|
||||
"disconnect": "",
|
||||
"microsoftSignIn": "",
|
||||
"signedIn": "",
|
||||
"title": ""
|
||||
},
|
||||
"cameraAndMic": "",
|
||||
"devices": "",
|
||||
"followMe": "",
|
||||
"language": "",
|
||||
"loggedIn": "",
|
||||
"moderator": "",
|
||||
"more": "",
|
||||
"name": "",
|
||||
"noDevice": "",
|
||||
"password": "",
|
||||
"selectAudioOutput": "",
|
||||
"selectCamera": "",
|
||||
"selectMic": "",
|
||||
"startAudioMuted": "",
|
||||
"startVideoMuted": "",
|
||||
"title": "",
|
||||
"update": ""
|
||||
},
|
||||
"settingsView": {
|
||||
"alertOk": "",
|
||||
"alertTitle": "",
|
||||
"alertURLText": "",
|
||||
"conferenceSection": "",
|
||||
"displayName": "",
|
||||
"email": "",
|
||||
"header": "",
|
||||
"profileSection": "",
|
||||
"serverURL": "",
|
||||
"startWithAudioMuted": "",
|
||||
"startWithVideoMuted": ""
|
||||
},
|
||||
"share": {
|
||||
"dialInfoText": "",
|
||||
"mainText": ""
|
||||
},
|
||||
"speaker": "",
|
||||
"speakerStats": {
|
||||
"hours": "",
|
||||
"minutes": "",
|
||||
"name": "",
|
||||
"seconds": "",
|
||||
"speakerStats": "",
|
||||
"speakerTime": ""
|
||||
},
|
||||
"startupoverlay": {
|
||||
"policyText": "",
|
||||
"title": ""
|
||||
},
|
||||
"suspendedoverlay": {
|
||||
"rejoinKeyTitle": "",
|
||||
"text": "",
|
||||
"title": ""
|
||||
},
|
||||
"toolbar": {
|
||||
"accessibilityLabel": {
|
||||
"audioOnly": "",
|
||||
"audioRoute": "",
|
||||
"callQuality": "",
|
||||
"cc": "",
|
||||
"chat": "",
|
||||
"document": "",
|
||||
"feedback": "",
|
||||
"fullScreen": "",
|
||||
"hangup": "",
|
||||
"invite": "",
|
||||
"kick": "",
|
||||
"localRecording": "",
|
||||
"lockRoom": "",
|
||||
"moreActions": "",
|
||||
"moreActionsMenu": "",
|
||||
"mute": "",
|
||||
"pip": "",
|
||||
"profile": "",
|
||||
"raiseHand": "",
|
||||
"recording": "",
|
||||
"remoteMute": "",
|
||||
"Settings": "",
|
||||
"sharedvideo": "",
|
||||
"shareRoom": "",
|
||||
"shareYourScreen": "",
|
||||
"shortcuts": "",
|
||||
"speakerStats": "",
|
||||
"tileView": "",
|
||||
"toggleCamera": "",
|
||||
"videomute": ""
|
||||
},
|
||||
"addPeople": "",
|
||||
"audioonly": "",
|
||||
"audioOnlyOff": "",
|
||||
"audioOnlyOn": "",
|
||||
"audioRoute": "",
|
||||
"authenticate": "",
|
||||
"callQuality": "",
|
||||
"cameraDisabled": "",
|
||||
"chat": "",
|
||||
"closeChat": "",
|
||||
"documentClose": "",
|
||||
"documentOpen": "",
|
||||
"enterFullScreen": "",
|
||||
"enterTileView": "",
|
||||
"etherpad": "",
|
||||
"exitFullScreen": "",
|
||||
"exitTileView": "",
|
||||
"feedback": "",
|
||||
"filmstrip": "",
|
||||
"fullscreen": "",
|
||||
"hangup": "",
|
||||
"invite": "",
|
||||
"lock": "",
|
||||
"login": "",
|
||||
"logout": "",
|
||||
"lowerYourHand": "",
|
||||
"micDisabled": "",
|
||||
"micMutedPopup": "",
|
||||
"moreActions": "",
|
||||
"mute": "",
|
||||
"openChat": "",
|
||||
"pip": "",
|
||||
"profile": "",
|
||||
"raiseHand": "",
|
||||
"raiseYourHand": "",
|
||||
"Settings": "",
|
||||
"sharedvideo": "",
|
||||
"sharedVideoMutedPopup": "",
|
||||
"shareRoom": "",
|
||||
"shortcuts": "",
|
||||
"sip": "",
|
||||
"speakerStats": "",
|
||||
"startScreenSharing": "",
|
||||
"startSubtitles": "",
|
||||
"stopScreenSharing": "",
|
||||
"stopSubtitles": "",
|
||||
"stopSharedVideo": "",
|
||||
"talkWhileMutedPopup": "",
|
||||
"tileViewToggle": "",
|
||||
"toggleCamera": "",
|
||||
"unableToUnmutePopup": "",
|
||||
"videomute": ""
|
||||
},
|
||||
"transcribing": {
|
||||
"ccButtonTooltip": "",
|
||||
"error": "",
|
||||
"expandedLabel": "",
|
||||
"failedToStart": "",
|
||||
"labelToolTip": "",
|
||||
"off": "",
|
||||
"pending": "",
|
||||
"start": "",
|
||||
"stop": "",
|
||||
"tr": ""
|
||||
},
|
||||
"userMedia": {
|
||||
"androidGrantPermissions": "",
|
||||
"chromeGrantPermissions": "",
|
||||
"edgeGrantPermissions": "",
|
||||
"electronGrantPermissions": "",
|
||||
"firefoxGrantPermissions": "",
|
||||
"iexplorerGrantPermissions": "",
|
||||
"nwjsGrantPermissions": "",
|
||||
"operaGrantPermissions": "",
|
||||
"react-nativeGrantPermissions": "",
|
||||
"safariGrantPermissions": ""
|
||||
},
|
||||
"videoSIPGW": {
|
||||
"busy": "",
|
||||
"busyTitle": "",
|
||||
"errorAlreadyInvited": "",
|
||||
"errorInvite": "",
|
||||
"errorInviteFailed": "",
|
||||
"errorInviteFailedTitle": "",
|
||||
"errorInviteTitle": "",
|
||||
"pending": "",
|
||||
"serviceName": "",
|
||||
"unavailableTitle": ""
|
||||
},
|
||||
"videoStatus": {
|
||||
"audioOnly": "",
|
||||
"audioOnlyExpanded": "",
|
||||
"callQuality": "",
|
||||
"hd": "",
|
||||
"hdTooltip": "",
|
||||
"highDefinition": "",
|
||||
"labelTooiltipNoVideo": "",
|
||||
"labelTooltipAudioOnly": "",
|
||||
"labelTooltipVideo": "",
|
||||
"ld": "",
|
||||
"ldTooltip": "",
|
||||
"lowDefinition": "",
|
||||
"onlyAudioAvailable": "",
|
||||
"onlyAudioSupported": "",
|
||||
"p2pEnabled": "",
|
||||
"p2pVideoQualityDescription": "",
|
||||
"qualityButtonTip": "",
|
||||
"recHighDefinitionOnly": "",
|
||||
"sd": "",
|
||||
"sdTooltip": "",
|
||||
"standardDefinition": ""
|
||||
},
|
||||
"videothumbnail": {
|
||||
"domute": "",
|
||||
"flip": "",
|
||||
"kick": "",
|
||||
"moderator": "",
|
||||
"mute": "",
|
||||
"muted": "",
|
||||
"remoteControl": "",
|
||||
"videomute": ""
|
||||
},
|
||||
"welcomepage": {
|
||||
"accessibilityLabel": {
|
||||
"join": "",
|
||||
"roomname": ""
|
||||
},
|
||||
"appDescription": "",
|
||||
"audioVideoSwitch": {
|
||||
"audio": "",
|
||||
"video": ""
|
||||
},
|
||||
"calendar": "",
|
||||
"connectCalendarButton": "",
|
||||
"connectCalendarText": "",
|
||||
"enterRoomTitle": "",
|
||||
"go": "",
|
||||
"join": "",
|
||||
"privacy": "",
|
||||
"recentList": "",
|
||||
"recentListDelete": "",
|
||||
"recentListEmpty": "",
|
||||
"roomname": "",
|
||||
"roomnameHint": "",
|
||||
"sendFeedback": "",
|
||||
"terms": "",
|
||||
"title": ""
|
||||
}
|
||||
}
|
||||
1185
lang/main-oc.json
1185
lang/main-oc.json
File diff suppressed because it is too large
Load Diff
1418
lang/main-ptBR.json
1418
lang/main-ptBR.json
File diff suppressed because it is too large
Load Diff
1178
lang/main-vi.json
1178
lang/main-vi.json
File diff suppressed because it is too large
Load Diff
@@ -50,7 +50,7 @@
|
||||
},
|
||||
"chat": {
|
||||
"error": "Error: your message \"__originalText__\" was not sent. Reason: __error__",
|
||||
"messagebox": "Type a message",
|
||||
"messagebox": "Enter text...",
|
||||
"nickname": {
|
||||
"popover": "Choose a nickname",
|
||||
"title": "Enter a nickname to use chat"
|
||||
@@ -121,7 +121,6 @@
|
||||
"deepLinking": {
|
||||
"appNotInstalled": "You need the __app__ mobile app to join this meeting on your phone.",
|
||||
"description": "Nothing happened? We tried launching your meeting in the __app__ desktop app. Try again or launch it in the __app__ web app.",
|
||||
"descriptionWithoutWeb": "Nothing happened? We tried launching your meeting in the __app__ desktop app.",
|
||||
"downloadApp": "Download the app",
|
||||
"launchWebButton": "Launch in web",
|
||||
"openApp": "Continue to the app",
|
||||
@@ -177,10 +176,10 @@
|
||||
"defaultError": "There was some kind of error",
|
||||
"detectext": "Error when trying to detect desktopsharing extension.",
|
||||
"dismiss": "Dismiss",
|
||||
"displayNameRequired": "Hi! What’s your name?",
|
||||
"displayNameRequired": "Display name is required",
|
||||
"done": "Done",
|
||||
"doNotShowMessageAgain": "Don't show this message again",
|
||||
"enterDisplayName": "Please enter your name here",
|
||||
"enterDisplayName": "Please enter your display name",
|
||||
"error": "Error",
|
||||
"externalInstallationMsg": "You need to install our desktop sharing extension.",
|
||||
"externalInstallationTitle": "Extension required",
|
||||
@@ -197,11 +196,11 @@
|
||||
"internalError": "Oops! Something went wrong. The following error occurred: __error__",
|
||||
"internalErrorTitle": "Internal error",
|
||||
"joinAgain": "Join again",
|
||||
"kickMessage": "You can contact __participantDisplayName__ for more details.",
|
||||
"kickMessage": "Ouch! You have been kicked out of the meet!",
|
||||
"kickParticipantButton": "Kick",
|
||||
"kickParticipantDialog": "Are you sure you want to kick this participant?",
|
||||
"kickParticipantTitle": "Kick this member?",
|
||||
"kickTitle": "Ouch! __participantDisplayName__ kicked you out of the meeting",
|
||||
"kickTitle": "Kicked from meeting",
|
||||
"liveStreaming": "Live Streaming",
|
||||
"liveStreamingDisabledForGuestTooltip": "Guests can't start live streaming.",
|
||||
"liveStreamingDisabledTooltip": "Start live stream disabled.",
|
||||
@@ -214,8 +213,8 @@
|
||||
"maxUsersLimitReachedTitle": "Maximum members limit reached",
|
||||
"micConstraintFailedError": "Your microphone does not satisfy some of the required constraints.",
|
||||
"micNotFoundError": "Microphone was not found.",
|
||||
"micNotSendingData": "Go to your computer's settings to unmute your mic and adjust its level",
|
||||
"micNotSendingDataTitle": "Your mic is muted by your system settings",
|
||||
"micNotSendingData": "We are unable to access your microphone. Please select another device from the settings menu or try to reload the application.",
|
||||
"micNotSendingDataTitle": "Unable to access microphone",
|
||||
"micPermissionDeniedError": "You have not granted permission to use your microphone. You can still join the conference but others won't hear you. Use the camera button in the address bar to fix this.",
|
||||
"micUnknownError": "Cannot use microphone for an unknown reason.",
|
||||
"muteParticipantBody": "You won't be able to unmute them, but they can unmute themselves at any time.",
|
||||
@@ -352,15 +351,13 @@
|
||||
"dialInConferenceID": "PIN:",
|
||||
"dialInNotSupported": "Sorry, dialing in is currently not supported.",
|
||||
"dialInNumber": "Dial-in:",
|
||||
"dialInSummaryError": "Error fetching dial-in info now. Please try again later.",
|
||||
"dialInTollFree": "Toll Free",
|
||||
"genericError": "Whoops, something went wrong.",
|
||||
"inviteLiveStream": "To view the live stream of this meeting, click this link: __url__",
|
||||
"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__",
|
||||
"inviteURLFirstPartGeneral": "You are invited to join a meeting.",
|
||||
"inviteURLFirstPartPersonal": "__name__ is inviting you to a meeting.\n",
|
||||
"inviteURLSecondPart": "\nJoin the meeting:\n__url__\n",
|
||||
"invitePhone": "One tap audio Dial In: __number__,,__conferenceID__#",
|
||||
"invitePhoneAlternatives": "Looking for a different dial in number? Please see: __url__",
|
||||
"inviteURL": "You are invited to join a meeting.\n__moreInfo__\nJoin meeting: __url__\n",
|
||||
"inviteURLMoreInfo": "Meeting ID: __conferenceID__#\n",
|
||||
"liveStreamURL": "Live stream:",
|
||||
"moreNumbers": "More numbers",
|
||||
"noNumbers": "No dial-in numbers.",
|
||||
@@ -472,25 +469,20 @@
|
||||
"focus": "Conference focus",
|
||||
"focusFail": "__component__ not available - retry in __ms__ sec",
|
||||
"grantedTo": "Moderator rights granted to __to__!",
|
||||
"kickParticipant": "__kicked__ was kicked by __kicker__",
|
||||
"me": "Me",
|
||||
"moderator": "Moderator rights granted!",
|
||||
"muted": "You have started the conversation muted.",
|
||||
"mutedTitle": "You're muted!",
|
||||
"mutedRemotelyTitle": "You have been muted by __participantDisplayName__!",
|
||||
"mutedRemotelyDescription": "You can always unmute when you're ready to speak. Mute back when you're done to keep noise away from the meeting.",
|
||||
"raisedHand": "__name__ would like to speak.",
|
||||
"somebody": "Somebody",
|
||||
"startSilentTitle": "You joined with no audio output!",
|
||||
"startSilentDescription": "Rejoin the meeting to enable audio",
|
||||
"suboptimalExperienceDescription": "Eer... we are afraid your experience with __appName__ isn't going to be that great here. We are looking for ways to improve this but, until then, please try using one of the <a href='static/recommendedBrowsers.html' target='_blank'>fully supported browsers</a>.",
|
||||
"suboptimalExperienceTitle": "Browser Warning",
|
||||
"newDeviceCameraTitle": "New camera detected",
|
||||
"newDeviceAudioTitle": "New audio device detected",
|
||||
"newDeviceMicTitle": "New microphone detected",
|
||||
"newDeviceCameraTitle": "New audio output detected",
|
||||
"newDeviceAction": "Use"
|
||||
},
|
||||
"passwordSetRemotely": "set by another member",
|
||||
"passwordDigitsOnly": "Up to __number__ digits",
|
||||
"poweredby": "powered by",
|
||||
"presenceStatus": {
|
||||
"busy": "Busy",
|
||||
@@ -781,7 +773,6 @@
|
||||
"enterRoomTitle": "Start a new meeting",
|
||||
"go": "GO",
|
||||
"join": "JOIN",
|
||||
"info": "Info",
|
||||
"privacy": "Privacy",
|
||||
"recentList": "Recent",
|
||||
"recentListDelete": "Delete",
|
||||
|
||||
@@ -5,10 +5,9 @@ import {
|
||||
createApiEvent,
|
||||
sendAnalytics
|
||||
} from '../../react/features/analytics';
|
||||
import { setPassword, setSubject } from '../../react/features/base/conference';
|
||||
import { setSubject } from '../../react/features/base/conference';
|
||||
import { parseJWTFromURLParams } from '../../react/features/base/jwt';
|
||||
import { invite } from '../../react/features/invite';
|
||||
import { toggleTileView } from '../../react/features/video-layout';
|
||||
import { getJitsiMeetTransport } from '../transport';
|
||||
|
||||
import { API_ID } from './constants';
|
||||
@@ -65,28 +64,6 @@ function initCommands() {
|
||||
sendAnalytics(createApiEvent('display.name.changed'));
|
||||
APP.conference.changeLocalDisplayName(displayName);
|
||||
},
|
||||
'password': password => {
|
||||
const { conference, passwordRequired }
|
||||
= APP.store.getState()['features/base/conference'];
|
||||
|
||||
if (passwordRequired) {
|
||||
sendAnalytics(createApiEvent('submit.password'));
|
||||
|
||||
APP.store.dispatch(setPassword(
|
||||
passwordRequired,
|
||||
passwordRequired.join,
|
||||
password
|
||||
));
|
||||
} else {
|
||||
sendAnalytics(createApiEvent('password.changed'));
|
||||
|
||||
APP.store.dispatch(setPassword(
|
||||
conference,
|
||||
conference.lock,
|
||||
password
|
||||
));
|
||||
}
|
||||
},
|
||||
'proxy-connection-event': event => {
|
||||
APP.conference.onProxyConnectionEvent(event);
|
||||
},
|
||||
@@ -120,14 +97,9 @@ function initCommands() {
|
||||
sendAnalytics(createApiEvent('screen.sharing.toggled'));
|
||||
toggleScreenSharing();
|
||||
},
|
||||
'toggle-tile-view': () => {
|
||||
sendAnalytics(createApiEvent('tile-view.toggled'));
|
||||
|
||||
APP.store.dispatch(toggleTileView());
|
||||
},
|
||||
'video-hangup': (showFeedbackDialog = true) => {
|
||||
'video-hangup': () => {
|
||||
sendAnalytics(createApiEvent('video.hangup'));
|
||||
APP.conference.hangup(showFeedbackDialog);
|
||||
APP.conference.hangup(true);
|
||||
},
|
||||
'email': email => {
|
||||
sendAnalytics(createApiEvent('email.changed'));
|
||||
@@ -581,38 +553,6 @@ class API {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application of an unexpected camera-related error having
|
||||
* occurred.
|
||||
*
|
||||
* @param {string} type - The type of the camera error.
|
||||
* @param {string} message - Additional information about the error.
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyOnCameraError(type: string, message: string) {
|
||||
this._sendEvent({
|
||||
name: 'camera-error',
|
||||
type,
|
||||
message
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application of an unexpected mic-related error having
|
||||
* occurred.
|
||||
*
|
||||
* @param {string} type - The type of the mic error.
|
||||
* @param {string} message - Additional information about the error.
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyOnMicError(type: string, message: string) {
|
||||
this._sendEvent({
|
||||
name: 'mic-error',
|
||||
type,
|
||||
message
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that conference feedback
|
||||
* has been submitted. Intended to be used in conjunction with the
|
||||
@@ -649,16 +589,6 @@ class API {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application of the current meeting requiring a password
|
||||
* to join.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyOnPasswordRequired() {
|
||||
this._sendEvent({ name: 'password-required' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that the screen sharing
|
||||
* has been turned on/off.
|
||||
@@ -692,21 +622,6 @@ class API {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that tile view has been
|
||||
* entered or exited.
|
||||
*
|
||||
* @param {string} enabled - True if tile view is currently displayed, false
|
||||
* otherwise.
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyTileViewChanged(enabled: boolean) {
|
||||
this._sendEvent({
|
||||
name: 'tile-view-changed',
|
||||
enabled
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes the allocated resources.
|
||||
*
|
||||
|
||||
8
modules/API/external/external_api.js
vendored
8
modules/API/external/external_api.js
vendored
@@ -33,14 +33,12 @@ const commands = {
|
||||
displayName: 'display-name',
|
||||
email: 'email',
|
||||
hangup: 'video-hangup',
|
||||
password: 'password',
|
||||
subject: 'subject',
|
||||
submitFeedback: 'submit-feedback',
|
||||
toggleAudio: 'toggle-audio',
|
||||
toggleChat: 'toggle-chat',
|
||||
toggleFilmStrip: 'toggle-film-strip',
|
||||
toggleShareScreen: 'toggle-share-screen',
|
||||
toggleTileView: 'toggle-tile-view',
|
||||
toggleVideo: 'toggle-video'
|
||||
};
|
||||
|
||||
@@ -52,7 +50,6 @@ const events = {
|
||||
'avatar-changed': 'avatarChanged',
|
||||
'audio-availability-changed': 'audioAvailabilityChanged',
|
||||
'audio-mute-status-changed': 'audioMuteStatusChanged',
|
||||
'camera-error': 'cameraError',
|
||||
'device-list-changed': 'deviceListChanged',
|
||||
'display-name-change': 'displayNameChange',
|
||||
'email-change': 'emailChange',
|
||||
@@ -60,11 +57,9 @@ const events = {
|
||||
'feedback-prompt-displayed': 'feedbackPromptDisplayed',
|
||||
'filmstrip-display-changed': 'filmstripDisplayChanged',
|
||||
'incoming-message': 'incomingMessage',
|
||||
'mic-error': 'micError',
|
||||
'outgoing-message': 'outgoingMessage',
|
||||
'participant-joined': 'participantJoined',
|
||||
'participant-left': 'participantLeft',
|
||||
'password-required': 'passwordRequired',
|
||||
'proxy-connection-event': 'proxyConnectionEvent',
|
||||
'video-ready-to-close': 'readyToClose',
|
||||
'video-conference-joined': 'videoConferenceJoined',
|
||||
@@ -72,8 +67,7 @@ const events = {
|
||||
'video-availability-changed': 'videoAvailabilityChanged',
|
||||
'video-mute-status-changed': 'videoMuteStatusChanged',
|
||||
'screen-sharing-status-changed': 'screenSharingStatusChanged',
|
||||
'subject-change': 'subjectChange',
|
||||
'tile-view-changed': 'tileViewChanged'
|
||||
'subject-change': 'subjectChange'
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
133
modules/UI/UI.js
133
modules/UI/UI.js
@@ -13,12 +13,16 @@ import SharedVideoManager from './shared_video/SharedVideo';
|
||||
import VideoLayout from './videolayout/VideoLayout';
|
||||
import Filmstrip from './videolayout/Filmstrip';
|
||||
|
||||
import { JitsiTrackErrors } from '../../react/features/base/lib-jitsi-meet';
|
||||
import { getLocalParticipant } from '../../react/features/base/participants';
|
||||
import { toggleChat } from '../../react/features/chat';
|
||||
import { openDisplayNamePrompt } from '../../react/features/display-name';
|
||||
import { setEtherpadHasInitialzied } from '../../react/features/etherpad';
|
||||
import { setFilmstripVisible } from '../../react/features/filmstrip';
|
||||
import { setNotificationsEnabled } from '../../react/features/notifications';
|
||||
import {
|
||||
setNotificationsEnabled,
|
||||
showWarningNotification
|
||||
} from '../../react/features/notifications';
|
||||
import {
|
||||
dockToolbox,
|
||||
setToolboxEnabled,
|
||||
@@ -36,6 +40,39 @@ UI.eventEmitter = eventEmitter;
|
||||
let etherpadManager;
|
||||
let sharedVideoManager;
|
||||
|
||||
const JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP = {
|
||||
microphone: {},
|
||||
camera: {}
|
||||
};
|
||||
|
||||
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
|
||||
.camera[JitsiTrackErrors.UNSUPPORTED_RESOLUTION]
|
||||
= 'dialog.cameraUnsupportedResolutionError';
|
||||
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[JitsiTrackErrors.GENERAL]
|
||||
= 'dialog.cameraUnknownError';
|
||||
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[JitsiTrackErrors.PERMISSION_DENIED]
|
||||
= 'dialog.cameraPermissionDeniedError';
|
||||
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[JitsiTrackErrors.NOT_FOUND]
|
||||
= 'dialog.cameraNotFoundError';
|
||||
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[JitsiTrackErrors.CONSTRAINT_FAILED]
|
||||
= 'dialog.cameraConstraintFailedError';
|
||||
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
|
||||
.camera[JitsiTrackErrors.NO_DATA_FROM_SOURCE]
|
||||
= 'dialog.cameraNotSendingData';
|
||||
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.microphone[JitsiTrackErrors.GENERAL]
|
||||
= 'dialog.micUnknownError';
|
||||
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
|
||||
.microphone[JitsiTrackErrors.PERMISSION_DENIED]
|
||||
= 'dialog.micPermissionDeniedError';
|
||||
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.microphone[JitsiTrackErrors.NOT_FOUND]
|
||||
= 'dialog.micNotFoundError';
|
||||
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
|
||||
.microphone[JitsiTrackErrors.CONSTRAINT_FAILED]
|
||||
= 'dialog.micConstraintFailedError';
|
||||
JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
|
||||
.microphone[JitsiTrackErrors.NO_DATA_FROM_SOURCE]
|
||||
= 'dialog.micNotSendingData';
|
||||
|
||||
const UIListeners = new Map([
|
||||
[
|
||||
UIEvents.ETHERPAD_CLICKED,
|
||||
@@ -99,6 +136,17 @@ UI.notifyReservationError = function(code, msg) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify user that he has been kicked from the server.
|
||||
*/
|
||||
UI.notifyKicked = function() {
|
||||
messageHandler.showError({
|
||||
hideErrorSupportLink: true,
|
||||
descriptionKey: 'dialog.kickMessage',
|
||||
titleKey: 'dialog.kickTitle'
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Notify user that conference was destroyed.
|
||||
* @param reason {string} the reason text
|
||||
@@ -247,6 +295,12 @@ UI.addLocalStream = track => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Removed remote stream from UI.
|
||||
* @param {JitsiTrack} track stream to remove
|
||||
*/
|
||||
UI.removeRemoteStream = track => VideoLayout.onRemoteStreamRemoved(track);
|
||||
|
||||
/**
|
||||
* Setup and show Etherpad.
|
||||
* @param {string} name etherpad id
|
||||
@@ -720,6 +774,83 @@ UI.showExtensionInlineInstallationDialog = function(callback) {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows a notifications about the passed in microphone error.
|
||||
*
|
||||
* @param {JitsiTrackError} micError - An error object related to using or
|
||||
* acquiring an audio stream.
|
||||
* @returns {void}
|
||||
*/
|
||||
UI.showMicErrorNotification = function(micError) {
|
||||
if (!micError) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { message, name } = micError;
|
||||
|
||||
const micJitsiTrackErrorMsg
|
||||
= JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.microphone[name];
|
||||
const micErrorMsg = micJitsiTrackErrorMsg
|
||||
|| JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
|
||||
.microphone[JitsiTrackErrors.GENERAL];
|
||||
const additionalMicErrorMsg = micJitsiTrackErrorMsg ? null : message;
|
||||
|
||||
APP.store.dispatch(showWarningNotification({
|
||||
description: additionalMicErrorMsg,
|
||||
descriptionKey: micErrorMsg,
|
||||
titleKey: name === JitsiTrackErrors.PERMISSION_DENIED
|
||||
? 'deviceError.microphonePermission'
|
||||
: 'deviceError.microphoneError'
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows a notifications about the passed in camera error.
|
||||
*
|
||||
* @param {JitsiTrackError} cameraError - An error object related to using or
|
||||
* acquiring a video stream.
|
||||
* @returns {void}
|
||||
*/
|
||||
UI.showCameraErrorNotification = function(cameraError) {
|
||||
if (!cameraError) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { message, name } = cameraError;
|
||||
|
||||
const cameraJitsiTrackErrorMsg
|
||||
= JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP.camera[name];
|
||||
const cameraErrorMsg = cameraJitsiTrackErrorMsg
|
||||
|| JITSI_TRACK_ERROR_TO_MESSAGE_KEY_MAP
|
||||
.camera[JitsiTrackErrors.GENERAL];
|
||||
const additionalCameraErrorMsg = cameraJitsiTrackErrorMsg ? null : message;
|
||||
|
||||
APP.store.dispatch(showWarningNotification({
|
||||
description: additionalCameraErrorMsg,
|
||||
descriptionKey: cameraErrorMsg,
|
||||
titleKey: name === JitsiTrackErrors.PERMISSION_DENIED
|
||||
? 'deviceError.cameraPermission' : 'deviceError.cameraError'
|
||||
}));
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows error dialog that informs the user that no data is received from the
|
||||
* device.
|
||||
*
|
||||
* @param {boolean} isAudioTrack - Whether or not the dialog is for an audio
|
||||
* track error.
|
||||
* @returns {void}
|
||||
*/
|
||||
UI.showTrackNotWorkingDialog = function(isAudioTrack) {
|
||||
messageHandler.showError({
|
||||
descriptionKey: isAudioTrack
|
||||
? 'dialog.micNotSendingData' : 'dialog.cameraNotSendingData',
|
||||
titleKey: isAudioTrack
|
||||
? 'dialog.micNotSendingDataTitle'
|
||||
: 'dialog.cameraNotSendingDataTitle'
|
||||
});
|
||||
};
|
||||
|
||||
UI.updateDevicesAvailability = function(id, devices) {
|
||||
VideoLayout.setDeviceAvailabilityIcons(id, devices);
|
||||
};
|
||||
|
||||
@@ -22,6 +22,7 @@ export default function SharedVideoThumb(participant, videoType, VideoLayout) {
|
||||
this.updateDisplayName();
|
||||
|
||||
this.container.onclick = this._onContainerClick;
|
||||
this.container.ondblclick = this._onContainerDoubleClick;
|
||||
}
|
||||
SharedVideoThumb.prototype = Object.create(SmallVideo.prototype);
|
||||
SharedVideoThumb.prototype.constructor = SharedVideoThumb;
|
||||
|
||||
@@ -361,12 +361,6 @@ const Filmstrip = {
|
||||
'min-width': `${local.thumbWidth}px`,
|
||||
width: `${local.thumbWidth}px`
|
||||
});
|
||||
|
||||
const avatarSize = local.thumbHeight / 2;
|
||||
|
||||
thumbs.localThumb.find('.avatar-container')
|
||||
.height(avatarSize)
|
||||
.width(avatarSize);
|
||||
}
|
||||
|
||||
if (thumbs.remoteThumbs) {
|
||||
@@ -377,12 +371,6 @@ const Filmstrip = {
|
||||
'min-width': `${remote.thumbWidth}px`,
|
||||
width: `${remote.thumbWidth}px`
|
||||
});
|
||||
|
||||
const avatarSize = remote.thumbHeight / 2;
|
||||
|
||||
thumbs.remoteThumbs.find('.avatar-container')
|
||||
.height(avatarSize)
|
||||
.width(avatarSize);
|
||||
}
|
||||
|
||||
const currentLayout = getCurrentLayout(APP.store.getState());
|
||||
|
||||
@@ -32,7 +32,7 @@ function LocalVideo(VideoLayout, emitter, streamEndedCallback) {
|
||||
|
||||
this.localVideoId = null;
|
||||
this.bindHoverHandler();
|
||||
if (!config.disableLocalVideoFlip) {
|
||||
if (config.enableLocalVideoFlip) {
|
||||
this._buildContextMenu();
|
||||
}
|
||||
this.isLocal = true;
|
||||
@@ -62,6 +62,7 @@ function LocalVideo(VideoLayout, emitter, streamEndedCallback) {
|
||||
this.updateIndicators();
|
||||
|
||||
this.container.onclick = this._onContainerClick;
|
||||
this.container.ondblclick = this._onContainerDoubleClick;
|
||||
}
|
||||
|
||||
LocalVideo.prototype = Object.create(SmallVideo.prototype);
|
||||
|
||||
@@ -89,6 +89,7 @@ function RemoteVideo(user, VideoLayout, emitter) {
|
||||
this._stopRemoteControl = this._stopRemoteControl.bind(this);
|
||||
|
||||
this.container.onclick = this._onContainerClick;
|
||||
this.container.ondblclick = this._onContainerDoubleClick;
|
||||
}
|
||||
|
||||
RemoteVideo.prototype = Object.create(SmallVideo.prototype);
|
||||
@@ -164,10 +165,7 @@ RemoteVideo.prototype._generatePopupContent = function() {
|
||||
|
||||
const initialVolumeValue
|
||||
= this._audioStreamElement && this._audioStreamElement.volume;
|
||||
|
||||
// hide volume when in silent mode
|
||||
const onVolumeChange = APP.store.getState()['features/base/config'].startSilent
|
||||
? undefined : this._setAudioVolume;
|
||||
const onVolumeChange = this._setAudioVolume;
|
||||
const { isModerator } = APP.conference;
|
||||
const participantID = this.id;
|
||||
|
||||
@@ -359,11 +357,6 @@ RemoteVideo.prototype.removeRemoteStreamElement = function(stream) {
|
||||
logger.info(`${isVideo ? 'Video' : 'Audio'
|
||||
} removed ${this.id}`, select);
|
||||
|
||||
|
||||
if (stream === this.videoStream) {
|
||||
this.videoStream = null;
|
||||
}
|
||||
|
||||
this.updateView();
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/* global $, APP, config, interfaceConfig */
|
||||
/* global $, APP, interfaceConfig */
|
||||
|
||||
/* eslint-disable no-unused-vars */
|
||||
import React from 'react';
|
||||
@@ -144,6 +144,7 @@ function SmallVideo(VideoLayout) {
|
||||
this.updateView = this.updateView.bind(this);
|
||||
|
||||
this._onContainerClick = this._onContainerClick.bind(this);
|
||||
this._onContainerDoubleClick = this._onContainerDoubleClick.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,8 +199,6 @@ SmallVideo.createStreamElement = function(stream) {
|
||||
|
||||
if (isVideo) {
|
||||
element.setAttribute('muted', 'true');
|
||||
} else if (config.startSilent) {
|
||||
element.muted = true;
|
||||
}
|
||||
|
||||
element.autoplay = true;
|
||||
@@ -858,6 +857,20 @@ SmallVideo.prototype.updateIndicators = function() {
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback invoked when the thumbnail is double clicked. Will pin the
|
||||
* participant if in tile view.
|
||||
*
|
||||
* @param {MouseEvent} event - The click event to intercept.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
SmallVideo.prototype._onContainerDoubleClick = function(event) {
|
||||
if (this._pinningRequiresDoubleClick() && this._shouldTriggerPin(event)) {
|
||||
APP.store.dispatch(pinParticipant(this.id));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback invoked when the thumbnail is clicked and potentially trigger
|
||||
* pinning of the participant.
|
||||
@@ -867,7 +880,8 @@ SmallVideo.prototype.updateIndicators = function() {
|
||||
* @returns {void}
|
||||
*/
|
||||
SmallVideo.prototype._onContainerClick = function(event) {
|
||||
const triggerPin = this._shouldTriggerPin(event);
|
||||
const triggerPin = this._shouldTriggerPin(event)
|
||||
&& !this._pinningRequiresDoubleClick();
|
||||
|
||||
if (event.stopPropagation && triggerPin) {
|
||||
event.stopPropagation();
|
||||
@@ -918,6 +932,17 @@ SmallVideo.prototype.togglePin = function() {
|
||||
APP.store.dispatch(pinParticipant(participantIdToPin));
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether or not clicking to pin the participant needs to be a double
|
||||
* click instead of a single click.
|
||||
*
|
||||
* @private
|
||||
* @returns {boolean}
|
||||
*/
|
||||
SmallVideo.prototype._pinningRequiresDoubleClick = function() {
|
||||
return shouldDisplayTileView(APP.store.getState());
|
||||
};
|
||||
|
||||
/**
|
||||
* Removes the React element responsible for showing connection status, dominant
|
||||
* speaker, and raised hand icons.
|
||||
|
||||
@@ -166,6 +166,22 @@ function getCameraVideoPosition( // eslint-disable-line max-params
|
||||
verticalIndent };
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of the video horizontal and vertical indents.
|
||||
* Centers horizontally and top aligns vertically.
|
||||
*
|
||||
* @return an array with 2 elements, the horizontal indent and the vertical
|
||||
* indent
|
||||
*/
|
||||
function getDesktopVideoPosition(videoWidth, videoHeight, videoSpaceWidth) {
|
||||
const horizontalIndent = (videoSpaceWidth - videoWidth) / 2;
|
||||
|
||||
const verticalIndent = 0;// Top aligned
|
||||
|
||||
return { horizontalIndent,
|
||||
verticalIndent };
|
||||
}
|
||||
|
||||
/**
|
||||
* Container for user video.
|
||||
*/
|
||||
@@ -350,23 +366,23 @@ export class VideoContainer extends LargeContainer {
|
||||
* @returns {{horizontalIndent, verticalIndent}}
|
||||
*/
|
||||
getVideoPosition(width, height, containerWidth, containerHeight) {
|
||||
let containerWidthToUse = containerWidth;
|
||||
|
||||
/* eslint-enable max-params */
|
||||
if (this.stream && this.isScreenSharing()) {
|
||||
let availableContainerWidth = containerWidth;
|
||||
|
||||
if (interfaceConfig.VERTICAL_FILMSTRIP) {
|
||||
containerWidthToUse -= Filmstrip.getFilmstripWidth();
|
||||
availableContainerWidth -= Filmstrip.getFilmstripWidth();
|
||||
}
|
||||
|
||||
return getCameraVideoPosition(width,
|
||||
return getDesktopVideoPosition(width,
|
||||
height,
|
||||
containerWidthToUse,
|
||||
availableContainerWidth,
|
||||
containerHeight);
|
||||
}
|
||||
|
||||
return getCameraVideoPosition(width,
|
||||
height,
|
||||
containerWidthToUse,
|
||||
containerWidth,
|
||||
containerHeight);
|
||||
|
||||
}
|
||||
|
||||
@@ -348,6 +348,10 @@ const VideoLayout = {
|
||||
remoteVideo.removeRemoteStreamElement(stream);
|
||||
}
|
||||
|
||||
if (stream.isVideoTrack()) {
|
||||
this._updateLargeVideoIfDisplayed(id);
|
||||
}
|
||||
|
||||
this.updateMutedForNoTracks(id, stream.getType());
|
||||
},
|
||||
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
/* global APP, JitsiMeetJS */
|
||||
|
||||
import {
|
||||
getAudioOutputDeviceId,
|
||||
notifyCameraError,
|
||||
notifyMicError
|
||||
} from '../../react/features/base/devices';
|
||||
import {
|
||||
getUserSelectedCameraDeviceId,
|
||||
getUserSelectedMicDeviceId,
|
||||
getUserSelectedOutputDeviceId
|
||||
} from '../../react/features/base/settings';
|
||||
import { getAudioOutputDeviceId } from '../../react/features/base/devices';
|
||||
|
||||
/**
|
||||
* Determines if currently selected audio output device should be changed after
|
||||
@@ -35,7 +26,8 @@ function getNewAudioOutputDevice(newDevices) {
|
||||
return 'default';
|
||||
}
|
||||
|
||||
const preferredAudioOutputDeviceId = getUserSelectedOutputDeviceId(APP.store.getState());
|
||||
const settings = APP.store.getState()['features/base/settings'];
|
||||
const preferredAudioOutputDeviceId = settings.userSelectedAudioOutputDeviceId;
|
||||
|
||||
// if the preferred one is not the selected and is available in the new devices
|
||||
// we want to use it as it was just added
|
||||
@@ -57,7 +49,8 @@ function getNewAudioOutputDevice(newDevices) {
|
||||
function getNewAudioInputDevice(newDevices, localAudio) {
|
||||
const availableAudioInputDevices = newDevices.filter(
|
||||
d => d.kind === 'audioinput');
|
||||
const selectedAudioInputDeviceId = getUserSelectedMicDeviceId(APP.store.getState());
|
||||
const settings = APP.store.getState()['features/base/settings'];
|
||||
const selectedAudioInputDeviceId = settings.userSelectedMicDeviceId;
|
||||
const selectedAudioInputDevice = availableAudioInputDevices.find(
|
||||
d => d.deviceId === selectedAudioInputDeviceId);
|
||||
|
||||
@@ -95,7 +88,8 @@ function getNewAudioInputDevice(newDevices, localAudio) {
|
||||
function getNewVideoInputDevice(newDevices, localVideo) {
|
||||
const availableVideoInputDevices = newDevices.filter(
|
||||
d => d.kind === 'videoinput');
|
||||
const selectedVideoInputDeviceId = getUserSelectedCameraDeviceId(APP.store.getState());
|
||||
const settings = APP.store.getState()['features/base/settings'];
|
||||
const selectedVideoInputDeviceId = settings.userSelectedCameraDeviceId;
|
||||
const selectedVideoInputDevice = availableVideoInputDevices.find(
|
||||
d => d.deviceId === selectedVideoInputDeviceId);
|
||||
|
||||
@@ -180,11 +174,11 @@ export default {
|
||||
]))
|
||||
.then(tracks => {
|
||||
if (audioTrackError) {
|
||||
APP.store.dispatch(notifyMicError(audioTrackError));
|
||||
APP.UI.showMicErrorNotification(audioTrackError);
|
||||
}
|
||||
|
||||
if (videoTrackError) {
|
||||
APP.store.dispatch(notifyCameraError(videoTrackError));
|
||||
APP.UI.showCameraErrorNotification(videoTrackError);
|
||||
}
|
||||
|
||||
return tracks.filter(t => typeof t !== 'undefined');
|
||||
@@ -209,7 +203,7 @@ export default {
|
||||
})
|
||||
.catch(err => {
|
||||
audioTrackError = err;
|
||||
showError && APP.store.disptach(notifyMicError(err));
|
||||
showError && APP.UI.showMicErrorNotification(err);
|
||||
|
||||
return [];
|
||||
}));
|
||||
@@ -227,7 +221,7 @@ export default {
|
||||
})
|
||||
.catch(err => {
|
||||
videoTrackError = err;
|
||||
showError && APP.store.dispatch(notifyCameraError(err));
|
||||
showError && APP.UI.showCameraErrorNotification(err);
|
||||
|
||||
return [];
|
||||
}));
|
||||
|
||||
92
package-lock.json
generated
92
package-lock.json
generated
@@ -2448,15 +2448,10 @@
|
||||
"isomorphic-fetch": "^2.2.1"
|
||||
}
|
||||
},
|
||||
"@react-native-community/async-storage": {
|
||||
"version": "1.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/async-storage/-/async-storage-1.3.4.tgz",
|
||||
"integrity": "sha512-fJmzL27x0BEjhmMXPnDPnUNCZK7bph+NBVCfAz9fzHzAamaiOkdUwuL3PvE4Oj4Kw4knP8ocw5VRDGorAidZ2g=="
|
||||
},
|
||||
"@react-native-community/cli": {
|
||||
"version": "1.9.4",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-1.9.4.tgz",
|
||||
"integrity": "sha512-7XjgqCdi23g6V7RV4tsYvqVqOBtNjAsWe5Oj2dR5KxDi3YqUyIyPjDWzyFkIxiO9XTGp9Al4QSmRwtOERvHO8A==",
|
||||
"version": "1.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-1.9.2.tgz",
|
||||
"integrity": "sha512-wSw3g6HrSUvLZiHiWRcO++JrKdbYNRWycGbGHVCnRLsdDRsj/y152xPlvBa29C8w+1SwiiN8aGsBOO0x9hkrCg==",
|
||||
"requires": {
|
||||
"chalk": "^1.1.1",
|
||||
"commander": "^2.19.0",
|
||||
@@ -5485,41 +5480,12 @@
|
||||
}
|
||||
},
|
||||
"errorhandler": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.1.tgz",
|
||||
"integrity": "sha512-rcOwbfvP1WTViVoUjcfZicVzjhjTuhSMntHh6mW3IrEiyE6mJyXvsToJUJGlGlw/2xU9P5whlWNGlIDVeCiT4A==",
|
||||
"version": "1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/errorhandler/-/errorhandler-1.5.0.tgz",
|
||||
"integrity": "sha1-6rpkyl1UKjEayUX1gt78M2Fl2fQ=",
|
||||
"requires": {
|
||||
"accepts": "~1.3.7",
|
||||
"accepts": "~1.3.3",
|
||||
"escape-html": "~1.0.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"accepts": {
|
||||
"version": "1.3.7",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz",
|
||||
"integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==",
|
||||
"requires": {
|
||||
"mime-types": "~2.1.24",
|
||||
"negotiator": "0.6.2"
|
||||
}
|
||||
},
|
||||
"mime-db": {
|
||||
"version": "1.40.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
|
||||
"integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA=="
|
||||
},
|
||||
"mime-types": {
|
||||
"version": "2.1.24",
|
||||
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
|
||||
"integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
|
||||
"requires": {
|
||||
"mime-db": "1.40.0"
|
||||
}
|
||||
},
|
||||
"negotiator": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
|
||||
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"es-abstract": {
|
||||
@@ -8946,8 +8912,8 @@
|
||||
}
|
||||
},
|
||||
"lib-jitsi-meet": {
|
||||
"version": "github:jitsi/lib-jitsi-meet#ee1854c1b12a229f70f351acaebe6686f38f6a85",
|
||||
"from": "github:jitsi/lib-jitsi-meet#ee1854c1b12a229f70f351acaebe6686f38f6a85",
|
||||
"version": "github:jitsi/lib-jitsi-meet#23503b0efd4b97989af7b10696ec9ae790aec7bb",
|
||||
"from": "github:jitsi/lib-jitsi-meet#23503b0efd4b97989af7b10696ec9ae790aec7bb",
|
||||
"requires": {
|
||||
"@jitsi/sdp-interop": "0.1.14",
|
||||
"@jitsi/sdp-simulcast": "0.2.1",
|
||||
@@ -12072,9 +12038,9 @@
|
||||
}
|
||||
},
|
||||
"react-native": {
|
||||
"version": "0.59.8",
|
||||
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.59.8.tgz",
|
||||
"integrity": "sha512-x1T+/pEXrjgdH9uDzd5doJy5aFlBqW04j7ljDKIGALchhnvdFbtXXrUZ/1PfWHMrIdZxtaDt4tkSttp662GSQA==",
|
||||
"version": "0.59.5",
|
||||
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.59.5.tgz",
|
||||
"integrity": "sha512-8Q/9cS6IMsGNiFhJgzmncbUeuacXQMe5EJl0c63fW30DvjEjeTVCvhM08eGzSpsNlOvL2XDRa4YOiCrwI7S1TA==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.0.0",
|
||||
"@react-native-community/cli": "^1.2.1",
|
||||
@@ -12219,9 +12185,9 @@
|
||||
"integrity": "sha512-kEzgZxbbXYhy27u5GnhrKitn+XDBFAHSDUJdYC6llMi5cDPjgcqhOAQABj0K+ga5pn+/xPZLmD882rrUGiwVVA=="
|
||||
},
|
||||
"react-native-google-signin": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-google-signin/-/react-native-google-signin-2.0.0.tgz",
|
||||
"integrity": "sha512-9loM4lcCIdbco5BnmNio7yGaXQKCpCaY1VRmYiTSvC5NjuSf6Ui6jZRee46p/YdaU4yRnS3u5Vct6Psrvr0HNg=="
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/react-native-google-signin/-/react-native-google-signin-1.0.2.tgz",
|
||||
"integrity": "sha512-4HPPSecI29gX0Pu7h2E7ZYXnKO4r+6eh5f+Unm67liE1RfvCQfOqoDliPbK96Mb/91VgHwqyxi0sUEC4j54/AQ=="
|
||||
},
|
||||
"react-native-immersive": {
|
||||
"version": "2.0.0",
|
||||
@@ -12308,25 +12274,6 @@
|
||||
"prop-types": "^15.5.10"
|
||||
}
|
||||
},
|
||||
"react-native-webview": {
|
||||
"version": "5.8.1",
|
||||
"resolved": "https://registry.npmjs.org/react-native-webview/-/react-native-webview-5.8.1.tgz",
|
||||
"integrity": "sha512-b6pSvmjoiWtcz6YspggW02X+BRXJWuquHwkh37BRx1NMW1iwMZA31SnFQvTpPzWYYIb9WF/mRsy2nGtt9C6NIg==",
|
||||
"requires": {
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"invariant": "2.2.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"invariant": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
||||
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-node-resolver": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/react-node-resolver/-/react-node-resolver-1.0.1.tgz",
|
||||
@@ -12398,15 +12345,6 @@
|
||||
"exenv": "^1.2.2"
|
||||
}
|
||||
},
|
||||
"react-textarea-autosize": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-7.1.0.tgz",
|
||||
"integrity": "sha512-c2FlR/fP0qbxmlrW96SdrbgP/v0XZMTupqB90zybvmDVDutytUgPl7beU35klwcTeMepUIQEpQUn3P3bdshGPg==",
|
||||
"requires": {
|
||||
"@babel/runtime": "^7.1.2",
|
||||
"prop-types": "^15.6.0"
|
||||
}
|
||||
},
|
||||
"react-transform-hmr": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-transform-hmr/-/react-transform-hmr-1.0.4.tgz",
|
||||
|
||||
@@ -34,7 +34,6 @@
|
||||
"@atlaskit/toggle": "5.0.14",
|
||||
"@atlaskit/tooltip": "12.1.13",
|
||||
"@microsoft/microsoft-graph-client": "1.1.0",
|
||||
"@react-native-community/async-storage": "1.3.4",
|
||||
"@webcomponents/url": "0.7.1",
|
||||
"amplitude-js": "4.5.2",
|
||||
"bc-css-flags": "3.0.0",
|
||||
@@ -52,7 +51,7 @@
|
||||
"js-utils": "github:jitsi/js-utils#73a67a7a60d52f8e895f50939c8fcbd1f20fe7b5",
|
||||
"jsrsasign": "8.0.12",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#ee1854c1b12a229f70f351acaebe6686f38f6a85",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#23503b0efd4b97989af7b10696ec9ae790aec7bb",
|
||||
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
|
||||
"lodash": "4.17.11",
|
||||
"moment": "2.19.4",
|
||||
@@ -63,12 +62,12 @@
|
||||
"react-emoji-render": "0.4.6",
|
||||
"react-i18next": "7.13.0",
|
||||
"react-linkify": "0.2.2",
|
||||
"react-native": "0.59.8",
|
||||
"react-native": "0.59.5",
|
||||
"react-native-background-timer": "2.1.1",
|
||||
"react-native-calendar-events": "1.6.4",
|
||||
"react-native-callstats": "3.58.2",
|
||||
"react-native-fast-image": "5.1.1",
|
||||
"react-native-google-signin": "2.0.0",
|
||||
"react-native-google-signin": "1.0.2",
|
||||
"react-native-immersive": "2.0.0",
|
||||
"react-native-keep-awake": "4.0.0",
|
||||
"react-native-linear-gradient": "2.5.3",
|
||||
@@ -77,9 +76,7 @@
|
||||
"react-native-vector-icons": "6.0.2",
|
||||
"react-native-watch-connectivity": "0.2.0",
|
||||
"react-native-webrtc": "github:jitsi/react-native-webrtc#4064c6f2db4f8b961daaaa8dafc6a896d7cfbc43",
|
||||
"react-native-webview": "5.8.1",
|
||||
"react-redux": "5.0.7",
|
||||
"react-textarea-autosize": "7.1.0",
|
||||
"react-transition-group": "2.4.0",
|
||||
"redux": "4.0.0",
|
||||
"redux-thunk": "2.2.0",
|
||||
|
||||
@@ -271,18 +271,6 @@ export function createInviteDialogEvent(
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an "offer/answer failure" event.
|
||||
*
|
||||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export function createOfferAnswerFailedEvent() {
|
||||
return {
|
||||
action: 'offer.answer.failure'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a "page reload" event.
|
||||
*
|
||||
@@ -384,28 +372,6 @@ export function createLiveStreamingDialogEvent(dialogName, buttonName) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event with the local tracks duration.
|
||||
*
|
||||
* @param {Object} duration - The object with the duration of the local tracks.
|
||||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export function createLocalTracksDurationEvent(duration) {
|
||||
const { audio, video, conference } = duration;
|
||||
const { camera, desktop } = video;
|
||||
|
||||
return {
|
||||
action: 'local.tracks.durations',
|
||||
attributes: {
|
||||
audio: audio.value,
|
||||
camera: camera.value,
|
||||
conference: conference.value,
|
||||
desktop: desktop.value
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event which indicates that an action related to recording has
|
||||
* occured.
|
||||
@@ -563,18 +529,6 @@ export function createStartAudioOnlyEvent(audioOnly) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event which indicates the "start silent" configuration.
|
||||
*
|
||||
* @returns {Object} The event in a format suitable for sending via
|
||||
* sendAnalytics.
|
||||
*/
|
||||
export function createStartSilentEvent() {
|
||||
return {
|
||||
action: 'start.silent'
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event which indicates the "start muted" configuration.
|
||||
*
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
// @flow
|
||||
|
||||
/**
|
||||
* The type of (redux) action which signals that local media duration has changed.
|
||||
*
|
||||
* {
|
||||
* type: UPDATE_LOCAL_TRACKS_DURATION,
|
||||
* localTracksDuration: Object
|
||||
* }
|
||||
*/
|
||||
export const UPDATE_LOCAL_TRACKS_DURATION = 'UPDATE_LOCAL_TRACKS_DURATION';
|
||||
@@ -15,7 +15,7 @@ export default class AmplitudeHandler extends AbstractHandler {
|
||||
constructor(options) {
|
||||
super();
|
||||
|
||||
const { amplitudeAPPKey, host, user } = options;
|
||||
const { amplitudeAPPKey, host } = options;
|
||||
|
||||
if (!amplitudeAPPKey) {
|
||||
throw new Error('Failed to initialize Amplitude handler, no APP key');
|
||||
@@ -28,10 +28,6 @@ export default class AmplitudeHandler extends AbstractHandler {
|
||||
};
|
||||
|
||||
amplitude.getInstance(this._amplitudeOptions).init(amplitudeAPPKey);
|
||||
|
||||
if (user) {
|
||||
amplitude.getInstance(this._amplitudeOptions).setUserId(user);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,16 +31,6 @@ class Amplitude {
|
||||
AmplitudeNative.init(this._instanceName, apiKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an identifier for the current user.
|
||||
*
|
||||
* @param {string} userId - The new user id.
|
||||
* @returns {void}
|
||||
*/
|
||||
setUserId(userId) {
|
||||
AmplitudeNative.setUserId(this._instanceName, userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets user properties for the current user.
|
||||
*
|
||||
|
||||
@@ -2,4 +2,3 @@ export * from './AnalyticsEvents';
|
||||
export * from './functions';
|
||||
|
||||
import './middleware';
|
||||
import './reducer';
|
||||
|
||||
@@ -1,74 +1,8 @@
|
||||
// @flow
|
||||
|
||||
import {
|
||||
CONFERENCE_JOINED,
|
||||
CONFERENCE_WILL_LEAVE,
|
||||
SET_ROOM
|
||||
} from '../base/conference';
|
||||
import { SET_ROOM } from '../base/conference';
|
||||
import { SET_CONFIG } from '../base/config';
|
||||
import { MiddlewareRegistry } from '../base/redux';
|
||||
import {
|
||||
getLocalAudioTrack,
|
||||
getLocalVideoTrack,
|
||||
TRACK_ADDED,
|
||||
TRACK_REMOVED,
|
||||
TRACK_UPDATED
|
||||
} from '../base/tracks';
|
||||
|
||||
import { UPDATE_LOCAL_TRACKS_DURATION } from './actionTypes';
|
||||
import { createLocalTracksDurationEvent } from './AnalyticsEvents';
|
||||
import { initAnalytics, resetAnalytics, sendAnalytics } from './functions';
|
||||
|
||||
/**
|
||||
* Calculates the duration of the local tracks.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {Object} - The local tracks duration.
|
||||
*/
|
||||
function calculateLocalTrackDuration(state) {
|
||||
const now = Date.now();
|
||||
const { localTracksDuration } = state['features/analytics'];
|
||||
const { conference } = state['features/base/conference'];
|
||||
const { audio, video } = localTracksDuration;
|
||||
const { camera, desktop } = video;
|
||||
const tracks = state['features/base/tracks'];
|
||||
const audioTrack = getLocalAudioTrack(tracks);
|
||||
const videoTrack = getLocalVideoTrack(tracks);
|
||||
const newDuration = { ...localTracksDuration };
|
||||
|
||||
if (!audioTrack || audioTrack.muted || !conference) {
|
||||
newDuration.audio = {
|
||||
startedTime: -1,
|
||||
value: audio.value + (audio.startedTime === -1 ? 0 : now - audio.startedTime)
|
||||
};
|
||||
} else if (audio.startedTime === -1) {
|
||||
newDuration.audio.startedTime = now;
|
||||
}
|
||||
|
||||
if (!videoTrack || videoTrack.muted || !conference) {
|
||||
newDuration.video = {
|
||||
camera: {
|
||||
startedTime: -1,
|
||||
value: camera.value + (camera.startedTime === -1 ? 0 : now - camera.startedTime)
|
||||
},
|
||||
desktop: {
|
||||
startedTime: -1,
|
||||
value: desktop.value + (desktop.startedTime === -1 ? 0 : now - desktop.startedTime)
|
||||
}
|
||||
};
|
||||
} else {
|
||||
const { videoType } = videoTrack;
|
||||
|
||||
if (video[videoType].startedTime === -1) {
|
||||
newDuration.video[videoType].startedTime = now;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...localTracksDuration,
|
||||
...newDuration
|
||||
};
|
||||
}
|
||||
import { initAnalytics, resetAnalytics } from './functions';
|
||||
|
||||
/**
|
||||
* Middleware which intercepts config actions to handle evaluating analytics
|
||||
@@ -78,81 +12,25 @@ function calculateLocalTrackDuration(state) {
|
||||
* @returns {Function}
|
||||
*/
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
if (action.type === SET_CONFIG) {
|
||||
switch (action.type) {
|
||||
case SET_CONFIG: {
|
||||
if (navigator.product === 'ReactNative') {
|
||||
// Reseting the analytics is currently not needed for web because
|
||||
// the user will be redirected to another page and new instance of
|
||||
// Analytics will be created and initialized.
|
||||
resetAnalytics();
|
||||
}
|
||||
}
|
||||
|
||||
const result = next(action);
|
||||
|
||||
switch (action.type) {
|
||||
case CONFERENCE_JOINED: {
|
||||
const { dispatch, getState } = store;
|
||||
const state = getState();
|
||||
|
||||
dispatch({
|
||||
type: UPDATE_LOCAL_TRACKS_DURATION,
|
||||
localTracksDuration: {
|
||||
...calculateLocalTrackDuration(state),
|
||||
conference: {
|
||||
startedTime: Date.now(),
|
||||
value: 0
|
||||
}
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
case CONFERENCE_WILL_LEAVE: {
|
||||
const { dispatch, getState } = store;
|
||||
const state = getState();
|
||||
const { localTracksDuration } = state['features/analytics'];
|
||||
const newLocalTracksDuration = {
|
||||
...calculateLocalTrackDuration(state),
|
||||
conference: {
|
||||
startedTime: -1,
|
||||
value: Date.now() - localTracksDuration.conference.startedTime
|
||||
}
|
||||
};
|
||||
|
||||
sendAnalytics(createLocalTracksDurationEvent(newLocalTracksDuration));
|
||||
|
||||
dispatch({
|
||||
type: UPDATE_LOCAL_TRACKS_DURATION,
|
||||
localTracksDuration: newLocalTracksDuration
|
||||
});
|
||||
break;
|
||||
}
|
||||
case SET_ROOM: {
|
||||
const result = next(action);
|
||||
|
||||
initAnalytics(store);
|
||||
break;
|
||||
}
|
||||
case TRACK_ADDED:
|
||||
case TRACK_REMOVED:
|
||||
case TRACK_UPDATED: {
|
||||
const { dispatch, getState } = store;
|
||||
const state = getState();
|
||||
const { localTracksDuration } = state['features/analytics'];
|
||||
|
||||
if (localTracksDuration.conference.startedTime === -1) {
|
||||
// We don't want to track the media duration if the conference is not joined yet because otherwise we won't
|
||||
// be able to compare them with the conference duration (from conference join to conference will leave).
|
||||
break;
|
||||
}
|
||||
dispatch({
|
||||
type: UPDATE_LOCAL_TRACKS_DURATION,
|
||||
localTracksDuration: {
|
||||
...localTracksDuration,
|
||||
...calculateLocalTrackDuration(state)
|
||||
}
|
||||
});
|
||||
break;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return next(action);
|
||||
});
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import { ReducerRegistry } from '../base/redux';
|
||||
|
||||
import { UPDATE_LOCAL_TRACKS_DURATION } from './actionTypes';
|
||||
|
||||
/**
|
||||
* Initial state.
|
||||
*/
|
||||
const DEFAULT_STATE = {
|
||||
localTracksDuration: {
|
||||
audio: {
|
||||
startedTime: -1,
|
||||
value: 0
|
||||
},
|
||||
video: {
|
||||
camera: {
|
||||
startedTime: -1,
|
||||
value: 0
|
||||
},
|
||||
desktop: {
|
||||
startedTime: -1,
|
||||
value: 0
|
||||
}
|
||||
},
|
||||
conference: {
|
||||
startedTime: -1,
|
||||
value: 0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Listen for actions which changes the state of the analytics feature.
|
||||
*
|
||||
* @param {Object} state - The Redux state of the feature features/analytics.
|
||||
* @param {Object} action - Action object.
|
||||
* @param {string} action.type - Type of action.
|
||||
* @returns {Object}
|
||||
*/
|
||||
ReducerRegistry.register('features/analytics', (state = DEFAULT_STATE, action) => {
|
||||
switch (action.type) {
|
||||
case UPDATE_LOCAL_TRACKS_DURATION:
|
||||
return {
|
||||
...state,
|
||||
localTracksDuration: action.localTracksDuration
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
});
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { appWillNavigate } from '../base/app';
|
||||
import { setRoom } from '../base/conference';
|
||||
import {
|
||||
configWillLoad,
|
||||
@@ -10,9 +11,8 @@ import {
|
||||
setConfig,
|
||||
storeConfig
|
||||
} from '../base/config';
|
||||
import { connect, disconnect, setLocationURL } from '../base/connection';
|
||||
import { setLocationURL } from '../base/connection';
|
||||
import { loadConfig } from '../base/lib-jitsi-meet';
|
||||
import { createDesiredLocalTracks } from '../base/tracks';
|
||||
import { parseURIString, toURLString } from '../base/util';
|
||||
import { setFatalError } from '../overlay';
|
||||
|
||||
@@ -59,11 +59,8 @@ export function appNavigate(uri: ?string) {
|
||||
const { contextRoot, host, room } = location;
|
||||
const locationURL = new URL(location.toString());
|
||||
|
||||
// Disconnect from any current conference.
|
||||
// FIXME: unify with web.
|
||||
if (navigator.product === 'ReactNative') {
|
||||
dispatch(disconnect());
|
||||
}
|
||||
// XXX this looks like CONFIG_WILL_LOAD ?
|
||||
dispatch(appWillNavigate(locationURL, room));
|
||||
|
||||
dispatch(configWillLoad(locationURL, room));
|
||||
|
||||
@@ -81,40 +78,25 @@ export function appNavigate(uri: ?string) {
|
||||
|
||||
let config;
|
||||
|
||||
// Avoid (re)loading the config when there is no room.
|
||||
if (!room) {
|
||||
try {
|
||||
config = await loadConfig(url);
|
||||
dispatch(storeConfig(baseURL, config));
|
||||
} catch (error) {
|
||||
config = restoreConfig(baseURL);
|
||||
}
|
||||
|
||||
if (!config) {
|
||||
try {
|
||||
config = await loadConfig(url);
|
||||
dispatch(storeConfig(baseURL, config));
|
||||
} catch (error) {
|
||||
config = restoreConfig(baseURL);
|
||||
if (!config) {
|
||||
dispatch(loadConfigError(error, locationURL, room));
|
||||
|
||||
if (!config) {
|
||||
dispatch(loadConfigError(error, locationURL));
|
||||
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (getState()['features/base/config'].locationURL !== locationURL) {
|
||||
dispatch(loadConfigError(new Error('Config no longer needed!'), locationURL));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(setLocationURL(locationURL));
|
||||
dispatch(setConfig(config));
|
||||
dispatch(setRoom(room));
|
||||
|
||||
// FIXME: unify with web, currently the connection and track creation happens in conference.js.
|
||||
if (room && navigator.product === 'ReactNative') {
|
||||
dispatch(createDesiredLocalTracks());
|
||||
dispatch(connect());
|
||||
if (getState()['features/base/config'].locationURL === locationURL) {
|
||||
dispatch(setLocationURL(locationURL));
|
||||
dispatch(setConfig(config));
|
||||
dispatch(setRoom(room));
|
||||
} else {
|
||||
dispatch(loadConfigError(new Error('Config no longer needed!'), locationURL, room));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,14 +6,12 @@ import '../../analytics';
|
||||
import '../../authentication';
|
||||
import { setColorScheme } from '../../base/color-scheme';
|
||||
import { DialogContainer } from '../../base/dialog';
|
||||
import { updateFlags } from '../../base/flags';
|
||||
import '../../base/jwt';
|
||||
import { Platform } from '../../base/react';
|
||||
import {
|
||||
AspectRatioDetector,
|
||||
ReducedUIDetector
|
||||
} from '../../base/responsive-ui';
|
||||
import { updateSettings } from '../../base/settings';
|
||||
import '../../google-api';
|
||||
import '../../mobile/audio-mode';
|
||||
import '../../mobile/background';
|
||||
@@ -49,14 +47,18 @@ type Props = AbstractAppProps & {
|
||||
externalAPIScope: string,
|
||||
|
||||
/**
|
||||
* An object with the feature flags.
|
||||
* Whether Picture-in-Picture is enabled. If {@code true}, a toolbar button
|
||||
* is rendered in the {@link Conference} view to afford entering
|
||||
* Picture-in-Picture.
|
||||
*/
|
||||
flags: Object,
|
||||
pictureInPictureEnabled: boolean,
|
||||
|
||||
/**
|
||||
* An object with user information (display name, email, avatar URL).
|
||||
* Whether the Welcome page is enabled. If {@code true}, the Welcome page is
|
||||
* rendered when the {@link App} is not at a location (URL) identifying
|
||||
* a Jitsi Meet conference/room.
|
||||
*/
|
||||
userInfo: ?Object
|
||||
welcomePageEnabled: boolean
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -94,12 +96,9 @@ export class App extends AbstractApp {
|
||||
super.componentDidMount();
|
||||
|
||||
this._init.then(() => {
|
||||
// We set these early enough so then we avoid any unnecessary re-renders.
|
||||
const { dispatch } = this.state.store;
|
||||
|
||||
dispatch(setColorScheme(this.props.colorScheme));
|
||||
dispatch(updateFlags(this.props.flags));
|
||||
dispatch(updateSettings(this.props.userInfo || {}));
|
||||
// We set the color scheme early enough so then we avoid any
|
||||
// unnecessary re-renders.
|
||||
this.state.store.dispatch(setColorScheme(this.props.colorScheme));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import React from 'react';
|
||||
import { DialogContainer } from '../../base/dialog';
|
||||
import '../../base/responsive-ui';
|
||||
import '../../chat';
|
||||
import '../../external-api';
|
||||
import '../../room-lock';
|
||||
import '../../video-layout';
|
||||
|
||||
|
||||
@@ -4,8 +4,9 @@ import { generateRoomWithoutSeparator } from 'js-utils/random';
|
||||
import type { Component } from 'react';
|
||||
|
||||
import { isRoomValid } from '../base/conference';
|
||||
import JitsiMeetJS from '../base/lib-jitsi-meet';
|
||||
import { Platform } from '../base/react';
|
||||
import { toState } from '../base/redux';
|
||||
import { isSupportedBrowser } from '../base/environment';
|
||||
import { Conference } from '../conference';
|
||||
import { getDeepLinkingPage } from '../deep-linking';
|
||||
import { UnsupportedDesktopBrowser } from '../unsupported-browser';
|
||||
@@ -39,109 +40,69 @@ export type Route = {
|
||||
*/
|
||||
export function _getRouteToRender(stateful: Function | Object): Promise<Route> {
|
||||
const state = toState(stateful);
|
||||
|
||||
if (navigator.product === 'ReactNative') {
|
||||
return _getMobileRoute(state);
|
||||
}
|
||||
|
||||
return _getWebConferenceRoute(state) || _getWebWelcomePageRoute(state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code Route} to display on the React Native app.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {Promise<Route>}
|
||||
*/
|
||||
function _getMobileRoute(state): Promise<Route> {
|
||||
const route = _getEmptyRoute();
|
||||
|
||||
if (isRoomValid(state['features/base/conference'].room)) {
|
||||
route.component = Conference;
|
||||
} else if (isWelcomePageAppEnabled(state)) {
|
||||
route.component = WelcomePage;
|
||||
} else {
|
||||
route.component = BlankPage;
|
||||
}
|
||||
|
||||
return Promise.resolve(route);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code Route} to display when trying to access a conference if
|
||||
* a valid conference is being joined.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {Promise<Route>|undefined}
|
||||
*/
|
||||
function _getWebConferenceRoute(state): ?Promise<Route> {
|
||||
if (!isRoomValid(state['features/base/conference'].room)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const route = _getEmptyRoute();
|
||||
|
||||
// Update the location if it doesn't match. This happens when a room is
|
||||
// joined from the welcome page. The reason for doing this instead of using
|
||||
// the history API is that we want to load the config.js which takes the
|
||||
// room into account.
|
||||
const { locationURL } = state['features/base/connection'];
|
||||
|
||||
if (window.location.href !== locationURL.href) {
|
||||
route.href = locationURL.href;
|
||||
|
||||
return Promise.resolve(route);
|
||||
}
|
||||
|
||||
return getDeepLinkingPage(state)
|
||||
.then(deepLinkComponent => {
|
||||
if (deepLinkComponent) {
|
||||
route.component = deepLinkComponent;
|
||||
} else if (isSupportedBrowser()) {
|
||||
route.component = Conference;
|
||||
} else {
|
||||
route.component = UnsupportedDesktopBrowser;
|
||||
}
|
||||
|
||||
return route;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@code Route} to display when trying to access the welcome page.
|
||||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {Promise<Route>}
|
||||
*/
|
||||
function _getWebWelcomePageRoute(state): Promise<Route> {
|
||||
const route = _getEmptyRoute();
|
||||
|
||||
if (isWelcomePageUserEnabled(state)) {
|
||||
if (isSupportedBrowser()) {
|
||||
route.component = WelcomePage;
|
||||
} else {
|
||||
route.component = UnsupportedDesktopBrowser;
|
||||
}
|
||||
} else {
|
||||
// Web: if the welcome page is disabled, go directly to a random room.
|
||||
|
||||
let href = window.location.href;
|
||||
|
||||
href.endsWith('/') || (href += '/');
|
||||
route.href = href + generateRoomWithoutSeparator();
|
||||
}
|
||||
|
||||
return Promise.resolve(route);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the default {@code Route}.
|
||||
*
|
||||
* @returns {Route}
|
||||
*/
|
||||
function _getEmptyRoute(): Route {
|
||||
return {
|
||||
const { room } = state['features/base/conference'];
|
||||
const isMobileApp = navigator.product === 'ReactNative';
|
||||
const isMobileBrowser
|
||||
= !isMobileApp && (Platform.OS === 'android' || Platform.OS === 'ios');
|
||||
const route: Route = {
|
||||
component: BlankPage,
|
||||
href: undefined
|
||||
};
|
||||
|
||||
return new Promise(resolve => {
|
||||
// First, check if the current endpoint supports WebRTC. We are
|
||||
// intentionally not performing the check for mobile browsers because:
|
||||
// - the WelcomePage is mobile ready;
|
||||
// - if the URL points to a conference, getDeepLinkingPage will take
|
||||
// care of it.
|
||||
if (!isMobileBrowser && !JitsiMeetJS.isWebRtcSupported()) {
|
||||
route.component = UnsupportedDesktopBrowser;
|
||||
resolve(route);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isRoomValid(room)) {
|
||||
if (isMobileApp) {
|
||||
route.component = Conference;
|
||||
resolve(route);
|
||||
} else {
|
||||
// Update the location if it doesn't match. This happens when a
|
||||
// room is joined from the welcome page. The reason for doing
|
||||
// this instead of using the history API is that we want to load
|
||||
// the config.js which takes the room into account.
|
||||
const { locationURL } = state['features/base/connection'];
|
||||
|
||||
// eslint-disable-next-line no-negated-condition
|
||||
if (window.location.href !== locationURL.href) {
|
||||
route.href = locationURL.href;
|
||||
resolve(route);
|
||||
} else {
|
||||
// Maybe show deep-linking, otherwise go to Conference.
|
||||
getDeepLinkingPage(state).then(component => {
|
||||
route.component = component || Conference;
|
||||
resolve(route);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isWelcomePageUserEnabled(state)) {
|
||||
// Web: if the welcome page is disabled, go directly to a random
|
||||
// room.
|
||||
|
||||
let href = window.location.href;
|
||||
|
||||
href.endsWith('/') || (href += '/');
|
||||
route.href = href + generateRoomWithoutSeparator();
|
||||
} else if (isWelcomePageAppEnabled(state)) {
|
||||
// Mobile: only go to the welcome page if enabled.
|
||||
|
||||
route.component = WelcomePage;
|
||||
}
|
||||
|
||||
resolve(route);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@ export function cancelWaitForOwner() {
|
||||
// clients/consumers need an event.
|
||||
const { authRequired } = getState()['features/base/conference'];
|
||||
|
||||
// FIXME remove when external-api ported to base/session
|
||||
authRequired && dispatch(conferenceLeft(authRequired));
|
||||
|
||||
dispatch(appNavigate(undefined));
|
||||
|
||||
@@ -64,14 +64,12 @@ class WaitForOwnerDialog extends Component<Props> {
|
||||
|
||||
return (
|
||||
<ConfirmDialog
|
||||
cancelKey = 'dialog.Cancel'
|
||||
contentKey = {
|
||||
{
|
||||
key: 'dialog.WaitForHostMsgWOk',
|
||||
params: { room }
|
||||
}
|
||||
}
|
||||
okKey = 'dialog.Ok'
|
||||
onCancel = { this._onCancel }
|
||||
onSubmit = { this._onLogin } />
|
||||
);
|
||||
|
||||
@@ -9,6 +9,13 @@
|
||||
*/
|
||||
export const APP_WILL_MOUNT = 'APP_WILL_MOUNT';
|
||||
|
||||
/**
|
||||
* FIXME.
|
||||
*
|
||||
* @type {string}
|
||||
*/
|
||||
export const APP_WILL_NAVIGATE = 'APP_WILL_NAVIGATE';
|
||||
|
||||
/**
|
||||
* The type of (redux) action which signals that a specific App will unmount (in
|
||||
* React terms).
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
import type { Dispatch } from 'redux';
|
||||
|
||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from './actionTypes';
|
||||
import { APP_WILL_MOUNT, APP_WILL_NAVIGATE, APP_WILL_UNMOUNT } from './actionTypes';
|
||||
|
||||
declare var APP;
|
||||
|
||||
@@ -17,6 +17,11 @@ declare var APP;
|
||||
*/
|
||||
export function appWillMount(app: Object) {
|
||||
return (dispatch: Dispatch<any>) => {
|
||||
dispatch({
|
||||
type: APP_WILL_MOUNT,
|
||||
app
|
||||
});
|
||||
|
||||
// TODO There was a redux action creator appInit which I did not like
|
||||
// because we already had the redux action creator appWillMount and,
|
||||
// respectively, the redux action APP_WILL_MOUNT. So I set out to remove
|
||||
@@ -25,11 +30,25 @@ export function appWillMount(app: Object) {
|
||||
// API module into its own feature yet so we're bound to work on that in
|
||||
// the future.
|
||||
typeof APP === 'object' && APP.API.init();
|
||||
};
|
||||
}
|
||||
|
||||
dispatch({
|
||||
type: APP_WILL_MOUNT,
|
||||
app
|
||||
});
|
||||
/**
|
||||
* FIXME.
|
||||
*
|
||||
* @param {URL} locationURL - FIXME.
|
||||
* @param {string} room - FIXME.
|
||||
* @returns {{
|
||||
* type: APP_WILL_NAVIGATE,
|
||||
* locationURL: URL,
|
||||
* room: ?string
|
||||
* }}
|
||||
*/
|
||||
export function appWillNavigate(locationURL: URL, room: ?string) {
|
||||
return {
|
||||
type: APP_WILL_NAVIGATE,
|
||||
locationURL,
|
||||
room
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -14,8 +14,6 @@ import {
|
||||
dominantSpeakerChanged,
|
||||
getNormalizedDisplayName,
|
||||
participantConnectionStatusChanged,
|
||||
participantKicked,
|
||||
participantMutedUs,
|
||||
participantPresenceChanged,
|
||||
participantRoleChanged,
|
||||
participantUpdated
|
||||
@@ -86,16 +84,10 @@ function _addConferenceListeners(conference, dispatch) {
|
||||
conference.on(
|
||||
JitsiConferenceEvents.CONFERENCE_LEFT,
|
||||
(...args) => dispatch(conferenceLeft(conference, ...args)));
|
||||
conference.on(JitsiConferenceEvents.SUBJECT_CHANGED,
|
||||
(...args) => dispatch(conferenceSubjectChanged(...args)));
|
||||
|
||||
conference.on(
|
||||
JitsiConferenceEvents.KICKED,
|
||||
(...args) => dispatch(kickedOut(conference, ...args)));
|
||||
|
||||
conference.on(
|
||||
JitsiConferenceEvents.PARTICIPANT_KICKED,
|
||||
(kicker, kicked) => dispatch(participantKicked(kicker, kicked)));
|
||||
() => dispatch(kickedOut(conference)));
|
||||
|
||||
conference.on(
|
||||
JitsiConferenceEvents.LOCK_STATE_CHANGED,
|
||||
@@ -135,14 +127,6 @@ function _addConferenceListeners(conference, dispatch) {
|
||||
JitsiConferenceEvents.TRACK_REMOVED,
|
||||
t => t && !t.isLocal() && dispatch(trackRemoved(t)));
|
||||
|
||||
conference.on(
|
||||
JitsiConferenceEvents.TRACK_MUTE_CHANGED,
|
||||
(_, participantThatMutedUs) => {
|
||||
if (participantThatMutedUs) {
|
||||
dispatch(participantMutedUs(participantThatMutedUs));
|
||||
}
|
||||
});
|
||||
|
||||
// Dispatches into features/base/participants follow:
|
||||
conference.on(
|
||||
JitsiConferenceEvents.DISPLAY_NAME_CHANGED,
|
||||
@@ -446,19 +430,15 @@ export function dataChannelOpened() {
|
||||
*
|
||||
* @param {JitsiConference} conference - The {@link JitsiConference} instance
|
||||
* for which the event is being signaled.
|
||||
* @param {JitsiParticipant} participant - The {@link JitsiParticipant}
|
||||
* instance which initiated the kick event.
|
||||
* @returns {{
|
||||
* type: KICKED_OUT,
|
||||
* conference: JitsiConference,
|
||||
* participant: JitsiParticipant
|
||||
* conference: JitsiConference
|
||||
* }}
|
||||
*/
|
||||
export function kickedOut(conference: Object, participant: Object) {
|
||||
export function kickedOut(conference: Object) {
|
||||
return {
|
||||
type: KICKED_OUT,
|
||||
conference,
|
||||
participant
|
||||
conference
|
||||
};
|
||||
}
|
||||
|
||||
@@ -777,6 +757,10 @@ export function setSubject(subject: string = '') {
|
||||
const { conference } = getState()['features/base/conference'];
|
||||
|
||||
if (conference) {
|
||||
dispatch({
|
||||
type: SET_PENDING_SUBJECT_CHANGE,
|
||||
subject: undefined
|
||||
});
|
||||
conference.setSubject(subject);
|
||||
} else {
|
||||
dispatch({
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
// @flow
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
import { JitsiTrackErrors } from '../lib-jitsi-meet';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
@@ -157,10 +155,10 @@ export function forEachConference(
|
||||
export function getConferenceName(stateful: Function | Object): string {
|
||||
const state = toState(stateful);
|
||||
const { callee } = state['features/base/jwt'];
|
||||
const { callDisplayName } = state['features/base/config'];
|
||||
const { pendingSubjectChange, room, subject } = state['features/base/conference'];
|
||||
|
||||
return pendingSubjectChange || subject || callDisplayName || (callee && callee.name) || _.startCase(room);
|
||||
return state['features/base/config'].callDisplayName
|
||||
|| (callee && callee.name)
|
||||
|| state['features/base/conference'].room;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,12 +6,10 @@ import {
|
||||
ACTION_UNPINNED,
|
||||
createAudioOnlyChangedEvent,
|
||||
createConnectionEvent,
|
||||
createOfferAnswerFailedEvent,
|
||||
createPinnedEvent,
|
||||
sendAnalytics
|
||||
} from '../../analytics';
|
||||
import { CONNECTION_ESTABLISHED, CONNECTION_FAILED } from '../connection';
|
||||
import { JitsiConferenceErrors } from '../lib-jitsi-meet';
|
||||
import { setVideoMuted, VIDEO_MUTISM_AUTHORITY } from '../media';
|
||||
import {
|
||||
getLocalParticipant,
|
||||
@@ -26,6 +24,7 @@ import { TRACK_ADDED, TRACK_REMOVED } from '../tracks';
|
||||
|
||||
import {
|
||||
conferenceFailed,
|
||||
conferenceLeft,
|
||||
conferenceWillLeave,
|
||||
createConference,
|
||||
setLastN,
|
||||
@@ -39,7 +38,6 @@ import {
|
||||
DATA_CHANNEL_OPENED,
|
||||
SET_AUDIO_ONLY,
|
||||
SET_LASTN,
|
||||
SET_PENDING_SUBJECT_CHANGE,
|
||||
SET_ROOM
|
||||
} from './actionTypes';
|
||||
import {
|
||||
@@ -154,12 +152,6 @@ StateListenerRegistry.register(
|
||||
function _conferenceFailed(store, next, action) {
|
||||
const result = next(action);
|
||||
|
||||
const { conference, error } = action;
|
||||
|
||||
if (error.name === JitsiConferenceErrors.OFFER_ANSWER_FAILED) {
|
||||
sendAnalytics(createOfferAnswerFailedEvent());
|
||||
}
|
||||
|
||||
// FIXME: Workaround for the web version. Currently, the creation of the
|
||||
// conference is handled by /conference.js and appropriate failure handlers
|
||||
// are set there.
|
||||
@@ -173,6 +165,8 @@ function _conferenceFailed(store, next, action) {
|
||||
}
|
||||
|
||||
// XXX After next(action), it is clear whether the error is recoverable.
|
||||
const { conference, error } = action;
|
||||
|
||||
!error.recoverable
|
||||
&& conference
|
||||
&& conference.leave().catch(reason => {
|
||||
@@ -335,17 +329,10 @@ function _connectionFailed({ dispatch, getState }, next, action) {
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _conferenceSubjectChanged({ dispatch, getState }, next, action) {
|
||||
function _conferenceSubjectChanged({ getState }, next, action) {
|
||||
const result = next(action);
|
||||
const { subject } = getState()['features/base/conference'];
|
||||
|
||||
if (subject) {
|
||||
dispatch({
|
||||
type: SET_PENDING_SUBJECT_CHANGE,
|
||||
subject: undefined
|
||||
});
|
||||
}
|
||||
|
||||
typeof APP === 'object' && APP.API.notifySubjectChanged(subject);
|
||||
|
||||
return result;
|
||||
@@ -580,30 +567,48 @@ function _setReceiverVideoConstraint(conference, preferred, max) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the feature base/conference that the action
|
||||
* {@code SET_ROOM} is being dispatched within a specific
|
||||
* redux store.
|
||||
* Notifies the feature {@code base/conference} that the redix action
|
||||
* {@link SET_ROOM} is being dispatched within a specific redux store.
|
||||
*
|
||||
* @param {Store} store - The redux store in which the specified {@code action}
|
||||
* is being dispatched.
|
||||
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
||||
* specified {@code action} to the specified {@code store}.
|
||||
* @param {Action} action - The redux action {@code SET_ROOM}
|
||||
* which is being dispatched in the specified {@code store}.
|
||||
* @param {Action} action - The redux action {@code SET_ROOM} which is being
|
||||
* dispatched in the specified {@code store}.
|
||||
* @private
|
||||
* @returns {Object} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _setRoom({ dispatch, getState }, next, action) {
|
||||
const state = getState();
|
||||
const { subject } = state['features/base/config'];
|
||||
const { room } = action;
|
||||
const result = next(action);
|
||||
|
||||
if (room) {
|
||||
// Set the stored subject.
|
||||
dispatch(setSubject(subject));
|
||||
// By the time SET_ROOM is dispatched, base/connection's locationURL and
|
||||
// base/conference's leaving should be the only conference-related sources
|
||||
// of truth.
|
||||
const state = getState();
|
||||
const { leaving } = state['features/base/conference'];
|
||||
const { locationURL } = state['features/base/connection'];
|
||||
const dispatchConferenceLeft = new Set();
|
||||
|
||||
// Figure out which of the JitsiConferences referenced by base/conference
|
||||
// have not dispatched or are not likely to dispatch CONFERENCE_FAILED and
|
||||
// CONFERENCE_LEFT.
|
||||
forEachConference(state, (conference, url) => {
|
||||
if (conference !== leaving && url && url !== locationURL) {
|
||||
dispatchConferenceLeft.add(conference);
|
||||
}
|
||||
|
||||
return true; // All JitsiConference instances are to be examined.
|
||||
});
|
||||
|
||||
// Dispatch CONFERENCE_LEFT for the JitsiConferences referenced by
|
||||
// base/conference which have not dispatched or are not likely to dispatch
|
||||
// CONFERENCE_FAILED or CONFERENCE_LEFT.
|
||||
for (const conference of dispatchConferenceLeft) {
|
||||
dispatch(conferenceLeft(conference));
|
||||
}
|
||||
|
||||
return next(action);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user