Compare commits

...

50 Commits

Author SHA1 Message Date
paweldomas
4d60a4e3a6 ref(base/session): createSession on APP_WILL_NAVIGATE 2019-05-08 14:48:53 -05:00
paweldomas
160380dac0 ref(base/session): create tracks and connect on SET_ROOM 2019-05-08 14:46:18 -05:00
paweldomas
9dbba7b119 ref(base/session): move endAllSessions to APP_WILL_NAVIGATE 2019-05-08 14:39:18 -05:00
paweldomas
7101f90b6e feat: add base/session 2019-05-06 16:06:56 -05:00
paweldomas
621ee7b447 ref(base/connection/actions.native): JitsiConnection.connect returns void
Do not return anything from JitsiConnection.connect, because it's not
a promise and returns void. Doing so is confusing to the reader.
2019-05-06 16:06:56 -05:00
Leonard Kim
3f4a71c26d fix(welcome-page): remove watermark container to avoid z-index wars
By making the container 100% height and position relative, that
would cause it to overlap any static-positioned  elements below it.
The 100% makes it so that any watermarks intended for the bottom
of the page show up on the bottom of the page. However, it's not
needed because watermark stylings already try to position the
watermarks at the bottom.
2019-05-04 11:03:48 -07:00
Leonard Kim
e7db8d6812 fix(chat): save chat error messages into redux
The proper field name is "messageType",
not "type." Also using "type" would
override the actionType.
2019-05-03 14:06:36 -07:00
Saúl Ibarra Corretgé
7d2ac0244d deps: react-native-webrtc@4064c6f2db4f8b961daaaa8dafc6a896d7cfbc43
New M69 build with Metal crash fixes.
2019-05-03 19:29:30 +02:00
Saúl Ibarra Corretgé
c0efea5168 ios: enable bitcode
Time has come. We need to enable bitcode. It's optional for iOS targets, but
mandatory for the entire project if there is a watchOS target. Since we have a
watchOS target, it's time to enable it.
2019-05-03 19:29:30 +02:00
Saúl Ibarra Corretgé
5ed53dcef5 ios: update Fastlane for watchOS app 2019-05-03 19:29:30 +02:00
Saúl Ibarra Corretgé
746159a1ac ios: set compilation mode to "wholemodule" for release builds 2019-05-03 19:29:30 +02:00
Saúl Ibarra Corretgé
43a8fd2a53 ios: set iOS deployment target correctly everywhere 2019-05-03 19:29:30 +02:00
Saúl Ibarra Corretgé
a26bb2c1a6 watchos: add watchOS app
Co-authored-by: Pawel Domas <pawel.domas@jitsi.org>
2019-05-03 19:29:30 +02:00
paweldomas
e4af5ddbe9 feat(base/connection): throw error and add isInviteURLReady 2019-05-03 19:29:30 +02:00
Дамян Минков
768cff48a4 Notify for new device (#4165)
* Fix detecting preferred audio output.

Fixes detecting when a new output device is found and we have stored user preference of using that device.

* Does not store which is the currently open device on save.

Does not save the currently opened device when saving settings dialog, this will be done once we successfully replace the tracks to use the new devices.

* Saves opened audio device after successfully changing it.

If we do it earlier _updateAudioDeviceId is using localAudio and can store wrong value.

* Adds notification for new non preferred devices.

A notification is shown which gives an option to the user to select and use the newly plugged devices.
Adding custom button and handler for the action to the notifications.

* Changes logic to search and handle all newly added devices from array.

* Moves some utility methods to features/base/devices.
2019-05-03 18:25:33 +01:00
damencho
384f0d4317 Removes hardcoded defaulting to US number from the code. 2019-05-03 16:55:22 +01:00
damencho
0ec4e6a805 Makes numbers clickable on the more numbers page when opened on mobile. 2019-05-03 16:55:22 +01:00
Saúl Ibarra Corretgé
5cc01b074e ios: update Podfile.lock for RN update 2019-05-03 10:27:17 +02:00
damencho
deaf5ba612 Always uses the id for the device extrackted from the track.
When updating the currently used devices always uses the id that is used by the local track instance.
2019-05-02 11:55:46 +01:00
damencho
740c1eb84f Adds new persistent state for devices user selection.
The state about currently opened devices is filtered and not stored, where we only store when user selects a device preferences.
Also allow changing input devices for Firefox when we are not in a conference.
2019-05-02 11:55:46 +01:00
Saúl Ibarra Corretgé
2d45709a6a android: add the ability to make a "libre" build
A libre build will exclude the following:

- Analytics modules
- Google Play services GMS
- Crashlytics
- Firebase
2019-05-02 09:26:20 +02:00
Saúl Ibarra Corretgé
6bbc2927ab analytics: don't initialize handlers if they are not properly configured 2019-05-02 09:26:20 +02:00
Saúl Ibarra Corretgé
08891b17b6 android: expose JitsiMeetActivity.leave() 2019-05-02 09:26:20 +02:00
Saúl Ibarra Corretgé
aab3428347 android: make JitsiMeetActivity.join public 2019-05-02 09:26:20 +02:00
Saúl Ibarra Corretgé
bf7b1c5cfc rn: add support for alpha.jitsi.net 2019-05-01 23:23:24 +02:00
Saúl Ibarra Corretgé
7b347baab6 deps: react-native@0.59.5 2019-05-01 19:16:08 +02:00
Saúl Ibarra Corretgé
f9b3d470e9 cc: fix showing CC button if config option is undefined 2019-04-30 14:50:56 +02:00
Saúl Ibarra Corretgé
7b78fa45f4 invite: don't consider "add people" enabled if there is no search URL
This avoids showing the + button when there is no service configured, ie with
the default Jitsi Meet install.
2019-04-30 14:50:56 +02:00
Saúl Ibarra Corretgé
34dcbd991e rn: wait for animation before hiding SlidingView 2019-04-30 12:45:53 +02:00
Saúl Ibarra Corretgé
70dc22c107 rn: refactor BottomSheet
Avoid using a Modal since those create trouble with the view hierarchy.
2019-04-30 12:45:53 +02:00
damencho
89719520e2 Disposes the tracks in component was unmounted while creating those.
The issue is if you quickly click Devices and then another tab, we may leave open tracks (video light stays on even when you are video muted).
2019-04-29 14:38:17 +00:00
Дамян Минков
ce7bdb35ac Merge pull request #4152 from jitsi/prosody-0.10-update
Updates config if prosody 0.10 is used.
2019-04-29 14:36:55 +00:00
Дамян Минков
bf8c716477 Merge pull request #4150 from jitsi/invitation-update
Update copy invite text.
2019-04-29 14:36:46 +00:00
Дамян Минков
93e8d755d3 Merge pull request #4148 from zbettenbuk/recording-web-css-fix
Safeguard Container style when used cross-platform
2019-04-29 14:36:35 +00:00
damencho
c09eee0985 Disables chat when we are in recorder mode. 2019-04-29 14:36:22 +00:00
damencho
4f6a0d7d3a Updates config if prosody 0.10 is used. 2019-04-29 15:24:55 +01:00
damencho
dd5233d31b Update copy invite text. 2019-04-29 15:06:36 +01:00
Bettenbuk Zoltan
31638133b7 Safeguard Container style when used cross-platform 2019-04-29 13:37:30 +02:00
Bettenbuk Zoltan
b886f8d72d rn: specify chat text field font color 2019-04-26 21:33:11 +02:00
Дамян Минков
a6555c5d24 Singleton follow me (#4144)
* Prints errors in case of wrong initialization.

Not printing can masks some errors in the code.

* Allow only one Follow Me moderator in a meeting.

* Sends Follow Me state with all presences of the moderator.

This fixes an issue where the moderator sends the Follow Me state and then for example mute or unmute video (this will produce a presence without Follow Me state) and the new comers will not reflect current Follow Me state till a change of it comes.

* Changes fixing comments.

* Changes fixing comments.
2019-04-26 18:11:53 +00:00
Hristo Terezov
98c8fb09c4 feat(package.json): Update lib-jitsi-meet. 2019-04-26 07:54:38 -07:00
Leonard Kim
b76b261cab fix(invite): show telephone icon 2019-04-26 07:46:41 -07:00
Bettenbuk Zoltan
0b6c51f666 rn: replace 3rd party chat library with custom implementation 2019-04-26 15:17:36 +02:00
Sylvia van Os
1cb9bbc7a4 Fix wrong filename change 2019-04-26 09:06:43 +00:00
Sylvia van Os
fee9bdb98c Fix NAT documentation 2019-04-26 09:06:43 +00:00
virtuacoplenny
fb82cf4517 fix(modal): bump dep to 8.0.1 to remove scrollbars (#4139) 2019-04-25 11:58:11 -07:00
Saúl Ibarra Corretgé
85388b8d23 ios: handle some corner cases with Firebase Dynamic Links
- handle some weird bug
(https://github.com/firebase/firebase-ios-sdk/issues/233)
- use a common function to extract the URL off a dynamic link
2019-04-25 18:18:09 +02:00
Saúl Ibarra Corretgé
33f133ac25 ios: simplify code for handling CallKit listeners
Replace the Swift array with an Objective-C one, since it's going to store
Objective-C objects and not Swift objects (or Swift objects which inherit from
NSObject, which is equivalent).

This avoids the need for JMCallKitEventListenerWrapper entirely, since an
NSArray can store NSObjectProtocol objects, unlike a Swift array, which prompted
the creation of the wrapper in the first place.
2019-04-25 18:17:55 +02:00
Saúl Ibarra Corretgé
774c5ecd18 rn: ensure the conference terminated event is always sent
Dear reader, I'm not proud at all of what you are about to read, but sometimes
life just gives you lemons, so enjoy some lemonade!

Joining a conference implies first creating the XMPP connection and then joining
the MUC. It's very possible the XMPP connection was made but there was no chance
for the conference to be created.

This patch fixes this case by artificially genrating a conference terminated
event in such case. In order to have all the necessary knowledge for this event
to be sent the connection now keeps track of the conference that runs it.

In addition, there is an even more obscure corner case: it's not impossible to
try to disconnect when there is not even a connection. This was fixed by
creating a fake disconnect event. Alas the location URL is lost at this point,
but it's better than nothing I guess.
2019-04-25 14:04:26 +02:00
Saúl Ibarra Corretgé
ccc5e19e3c rn: fix filling LoadConfigOverlay's background 2019-04-24 17:41:28 +02:00
142 changed files with 3959 additions and 803 deletions

View File

@@ -1,6 +1,7 @@
apply plugin: 'com.android.application'
boolean googleServicesEnabled = project.file('google-services.json').exists()
boolean googleServicesEnabled \
= project.file('google-services.json').exists() && !rootProject.ext.libreBuild
// Crashlytics integration is done as part of Firebase now, so it gets
// automagically activated with google-services.json
@@ -36,11 +37,24 @@ android {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules-debug.pro'
buildConfigField "boolean", "GOOGLE_SERVICES_ENABLED", "${googleServicesEnabled}"
buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
}
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules-release.pro'
buildConfigField "boolean", "GOOGLE_SERVICES_ENABLED", "${googleServicesEnabled}"
buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
}
}
sourceSets {
main {
java {
if (rootProject.ext.libreBuild) {
srcDir "src"
exclude "**/GoogleServicesHelper.java"
}
}
}
}
@@ -58,7 +72,17 @@ dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "com.android.support:support-v4:${rootProject.ext.supportLibVersion}"
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
implementation 'com.google.android.gms:play-services-auth:16.0.1'
if (!rootProject.ext.libreBuild) {
implementation 'com.google.android.gms:play-services-auth:16.0.1'
// Firebase
// - Crashlytics
// - Dynamic Links
implementation 'com.google.firebase:firebase-core:16.0.6'
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.8'
implementation 'com.google.firebase:firebase-dynamic-links:16.1.5'
}
implementation project(':sdk')
@@ -73,13 +97,6 @@ dependencies {
exclude group: "com.android.support", module: "annotations"
}
annotationProcessor "com.github.bumptech.glide:compiler:${rootProject.ext.glideVersion}"
// Firebase
// - Crashlytics
// - Dynamic Links
implementation 'com.google.firebase:firebase-core:16.0.6'
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.8'
implementation 'com.google.firebase:firebase-dynamic-links:16.1.5'
}
gradle.projectsEvaluated {

View File

@@ -80,6 +80,7 @@
# Jisti Meet SDK
-keep class org.jitsi.meet.** { *; }
-keep class org.jitsi.meet.sdk.** { *; }
# We added the following when we switched minifyEnabled on. Probably because we

View File

@@ -25,6 +25,7 @@
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:host="alpha.jitsi.net" android:scheme="https" />
<data android:host="beta.meet.jit.si" android:scheme="https" />
<data android:host="meet.jit.si" android:scheme="https" />
</intent-filter>

View File

@@ -0,0 +1,40 @@
package org.jitsi.meet;
import android.net.Uri;
import android.util.Log;
import com.crashlytics.android.Crashlytics;
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks;
import io.fabric.sdk.android.Fabric;
import org.jitsi.meet.sdk.JitsiMeetActivity;
/**
* Helper class to initialize Google related services and functionality.
* This functionality is compiled conditionally and called via reflection, that's why it was
* extracted here.
*
* "Libre builds" (builds with the LIBRE_BUILD flag set) will not include this file.
*/
final class GoogleServicesHelper {
public static void initialize(JitsiMeetActivity activity) {
if (BuildConfig.GOOGLE_SERVICES_ENABLED) {
Log.d(activity.getClass().getSimpleName(), "Initializing Google Services");
Fabric.with(activity, new Crashlytics());
FirebaseDynamicLinks.getInstance().getDynamicLink(activity.getIntent())
.addOnSuccessListener(activity, pendingDynamicLinkData -> {
Uri dynamicLink = null;
if (pendingDynamicLinkData != null) {
dynamicLink = pendingDynamicLinkData.getLink();
}
if (dynamicLink != null) {
activity.join(dynamicLink.toString());
}
});
}
}
}

View File

@@ -29,10 +29,7 @@ import org.jitsi.meet.sdk.JitsiMeet;
import org.jitsi.meet.sdk.JitsiMeetActivity;
import org.jitsi.meet.sdk.JitsiMeetConferenceOptions;
import com.crashlytics.android.Crashlytics;
import com.google.firebase.dynamiclinks.FirebaseDynamicLinks;
import io.fabric.sdk.android.Fabric;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;
@@ -57,22 +54,16 @@ public class MainActivity extends JitsiMeetActivity {
@Override
protected boolean extraInitialize() {
Log.d(this.getClass().getSimpleName(), "LIBRE_BUILD="+BuildConfig.LIBRE_BUILD);
// Setup Crashlytics and Firebase Dynamic Links
if (BuildConfig.GOOGLE_SERVICES_ENABLED) {
Fabric.with(this, new Crashlytics());
FirebaseDynamicLinks.getInstance().getDynamicLink(getIntent())
.addOnSuccessListener(this, pendingDynamicLinkData -> {
Uri dynamicLink = null;
if (pendingDynamicLinkData != null) {
dynamicLink = pendingDynamicLinkData.getLink();
}
if (dynamicLink != null) {
join(dynamicLink.toString());
}
});
// Here we are using reflection since it may have been disabled at compile time.
try {
Class<?> cls = Class.forName("org.jitsi.meet.GoogleServicesHelper");
Method m = cls.getMethod("initialize", JitsiMeetActivity.class);
m.invoke(null, this);
} catch (Exception e) {
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
}
// In Debug builds React needs permission to write over other apps in

View File

@@ -167,6 +167,9 @@ ext {
// Glide
excludeAppGlideModule = true
glideVersion = "4.7.1" // keep in sync with react-native-fast-image
// Libre build
libreBuild = (System.env.LIBRE_BUILD ?: "true").toBoolean()
}
// If Android SDK is not installed, accept its license so that it

View File

@@ -10,10 +10,25 @@ android {
}
buildTypes {
debug {}
debug {
buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField "boolean", "LIBRE_BUILD", "${rootProject.ext.libreBuild}"
}
}
sourceSets {
main {
java {
if (rootProject.ext.libreBuild) {
srcDir "src"
exclude "**/AmplitudeModule.java"
}
exclude "test/"
}
}
}
}
@@ -24,19 +39,23 @@ dependencies {
implementation "com.android.support:support-v4:${rootProject.ext.supportLibVersion}"
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
implementation 'com.amplitude:android-sdk:2.14.1'
implementation 'com.dropbox.core:dropbox-core-sdk:3.0.8'
api 'com.facebook.react:react-native:+'
implementation 'com.dropbox.core:dropbox-core-sdk:3.0.8'
if (!rootProject.ext.libreBuild) {
implementation 'com.amplitude:android-sdk:2.14.1'
implementation(project(":react-native-google-signin")) {
exclude group: 'com.google.android.gms'
exclude group: 'com.android.support'
}
}
implementation project(':react-native-background-timer')
implementation project(':react-native-calendar-events')
implementation(project(':react-native-fast-image')) {
exclude group: 'com.android.support'
}
implementation(project(":react-native-google-signin")) {
exclude group: 'com.google.android.gms'
exclude group: 'com.android.support'
}
implementation project(':react-native-immersive')
implementation project(':react-native-keep-awake')
implementation project(':react-native-linear-gradient')

View File

@@ -75,6 +75,7 @@ class AppInfoModule
constants.put(
"version",
packageInfo == null ? "" : packageInfo.versionName);
constants.put("LIBRE_BUILD", BuildConfig.LIBRE_BUILD);
return constants;
}

View File

@@ -73,7 +73,7 @@ public class JitsiMeetActivity extends FragmentActivity
@Override
public void finish() {
getJitsiView().leave();
leave();
super.finish();
}
@@ -87,7 +87,7 @@ public class JitsiMeetActivity extends FragmentActivity
return fragment.getJitsiView();
}
protected void join(@Nullable String url) {
public void join(@Nullable String url) {
JitsiMeetConferenceOptions options
= new JitsiMeetConferenceOptions.Builder()
.setRoom(url)
@@ -95,10 +95,14 @@ public class JitsiMeetActivity extends FragmentActivity
join(options);
}
protected void join(JitsiMeetConferenceOptions options) {
public void join(JitsiMeetConferenceOptions options) {
getJitsiView().join(options);
}
public void leave() {
getJitsiView().leave();
}
private @Nullable JitsiMeetConferenceOptions getConferenceOptions(Intent intent) {
String action = intent.getAction();

View File

@@ -21,6 +21,7 @@ import android.app.Application;
import android.support.annotation.Nullable;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactApplicationContext;
@@ -28,6 +29,7 @@ import com.facebook.react.common.LifecycleState;
import com.facebook.react.devsupport.DevInternalSettings;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -48,7 +50,6 @@ class ReactInstanceManagerHolder {
ReactApplicationContext reactContext) {
List<NativeModule> nativeModules
= new ArrayList<>(Arrays.<NativeModule>asList(
new AmplitudeModule(reactContext),
new AndroidSettingsModule(reactContext),
new AppInfoModule(reactContext),
new AudioModeModule(reactContext),
@@ -64,6 +65,14 @@ class ReactInstanceManagerHolder {
nativeModules.add(new RNConnectionService(reactContext));
}
try {
Class<?> amplitudeModuleClass = Class.forName("AmplitudeModule");
Constructor constructor = amplitudeModuleClass.getConstructor(ReactApplicationContext.class);
nativeModules.add((NativeModule)constructor.newInstance(reactContext));
} catch (Exception e) {
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
}
return nativeModules;
}
@@ -128,31 +137,39 @@ class ReactInstanceManagerHolder {
return;
}
List<ReactPackage> packages
= new ArrayList<>(Arrays.asList(
new com.BV.LinearGradient.LinearGradientPackage(),
new com.calendarevents.CalendarEventsPackage(),
new com.corbt.keepawake.KCKeepAwakePackage(),
new com.dylanvann.fastimage.FastImageViewPackage(),
new com.facebook.react.shell.MainReactPackage(),
new com.oblador.vectoricons.VectorIconsPackage(),
new com.ocetnik.timer.BackgroundTimerPackage(),
new com.oney.WebRTCModule.WebRTCModulePackage(),
new com.rnimmersive.RNImmersivePackage(),
new com.zmxv.RNSound.RNSoundPackage(),
new ReactPackageAdapter() {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return ReactInstanceManagerHolder.createNativeModules(reactContext);
}
}));
try {
Class<?> googlePackageClass = Class.forName("co.apptailor.googlesignin.RNGoogleSigninPackage");
Constructor constructor = googlePackageClass.getConstructor();
packages.add((ReactPackage)constructor.newInstance());
} catch (Exception e) {
// Ignore any error, the module is not compiled when LIBRE_BUILD is enabled.
}
reactInstanceManager
= ReactInstanceManager.builder()
.setApplication(application)
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index.android")
.addPackage(new co.apptailor.googlesignin.RNGoogleSigninPackage())
.addPackage(new com.BV.LinearGradient.LinearGradientPackage())
.addPackage(new com.calendarevents.CalendarEventsPackage())
.addPackage(new com.corbt.keepawake.KCKeepAwakePackage())
.addPackage(new com.dylanvann.fastimage.FastImageViewPackage())
.addPackage(new com.facebook.react.shell.MainReactPackage())
.addPackage(new com.oblador.vectoricons.VectorIconsPackage())
.addPackage(new com.ocetnik.timer.BackgroundTimerPackage())
.addPackage(new com.oney.WebRTCModule.WebRTCModulePackage())
.addPackage(new com.rnimmersive.RNImmersivePackage())
.addPackage(new com.zmxv.RNSound.RNSoundPackage())
.addPackage(new ReactPackageAdapter() {
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
return
ReactInstanceManagerHolder.createNativeModules(
reactContext);
}
})
.addPackages(packages)
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();

View File

@@ -49,6 +49,7 @@ import {
setDesktopSharingEnabled
} from './react/features/base/conference';
import {
checkAndNotifyForNewDevice,
getAvailableDevices,
setAudioOutputDeviceId,
updateDeviceList
@@ -437,8 +438,8 @@ class ConferenceConnector {
hasRead: true,
error: code,
message: msg,
timestamp: Date.now(),
type: 'error'
messageType: 'error',
timestamp: Date.now()
}));
}
break;
@@ -2094,9 +2095,7 @@ export default {
})
.then(() => {
logger.log('switched local video device');
APP.store.dispatch(updateSettings({
cameraDeviceId
}));
this._updateVideoDeviceId();
})
.catch(err => {
APP.UI.showCameraErrorNotification(err);
@@ -2125,12 +2124,11 @@ export default {
return stream;
})
.then(stream => {
this.useAudioStream(stream);
.then(stream => this.useAudioStream(stream))
.then(() => {
logger.log('switched local audio device');
APP.store.dispatch(updateSettings({
micDeviceId
}));
this._updateAudioDeviceId();
})
.catch(err => {
APP.UI.showMicErrorNotification(err);
@@ -2301,18 +2299,9 @@ export default {
// Ugly way to synchronize real device IDs with local
// storage and settings menu. This is a workaround until
// getConstraints() method will be implemented in browsers.
if (this.localAudio) {
dispatch(updateSettings({
micDeviceId: this.localAudio.getDeviceId()
}));
}
this._updateAudioDeviceId();
if (this.localVideo
&& this.localVideo.videoType === 'camera') {
dispatch(updateSettings({
cameraDeviceId: this.localVideo.getDeviceId()
}));
}
this._updateVideoDeviceId();
APP.UI.onAvailableDevicesChanged(devices);
});
@@ -2321,6 +2310,33 @@ export default {
return Promise.resolve();
},
/**
* Updates the settings for the currently used video device, extracting
* the device id from the used track.
* @private
*/
_updateVideoDeviceId() {
if (this.localVideo
&& this.localVideo.videoType === 'camera') {
APP.store.dispatch(updateSettings({
cameraDeviceId: this.localVideo.getDeviceId()
}));
}
},
/**
* Updates the settings for the currently used audio device, extracting
* the device id from the used track.
* @private
*/
_updateAudioDeviceId() {
if (this.localAudio) {
APP.store.dispatch(updateSettings({
micDeviceId: this.localAudio.getDeviceId()
}));
}
},
/**
* Event listener for JitsiMediaDevicesEvents.DEVICE_LIST_CHANGED to
* handle change of available media devices.
@@ -2329,6 +2345,8 @@ export default {
* @returns {Promise}
*/
_onDeviceListChanged(devices) {
const oldDevices = APP.store.getState()['features/base/devices'].availableDevices;
APP.store.dispatch(updateDeviceList(devices));
const newDevices
@@ -2367,6 +2385,25 @@ export default {
this.localVideo.stopStream();
}
// Let's handle unknown/non-preferred devices
const newAvailDevices
= APP.store.getState()['features/base/devices'].availableDevices;
if (typeof newDevices.audiooutput === 'undefined') {
APP.store.dispatch(
checkAndNotifyForNewDevice(newAvailDevices.audioOutput, oldDevices.audioOutput));
}
if (!requestedInput.audio) {
APP.store.dispatch(
checkAndNotifyForNewDevice(newAvailDevices.audioInput, oldDevices.audioInput));
}
if (!requestedInput.video) {
APP.store.dispatch(
checkAndNotifyForNewDevice(newAvailDevices.videoInput, oldDevices.videoInput));
}
promises.push(
mediaDeviceHelper.createLocalTracksAfterDeviceListChanged(
createLocalTracksF,
@@ -2394,7 +2431,12 @@ export default {
: this.useVideoStream.bind(this);
// Use the new stream or null if we failed to obtain it.
return useStream(tracks.find(track => track.getType() === mediaType) || null);
return useStream(tracks.find(track => track.getType() === mediaType) || null)
.then(() => {
mediaType === 'audio'
? this._updateAudioDeviceId()
: this._updateVideoDeviceId();
});
}
return Promise.resolve();

View File

@@ -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,

View File

@@ -162,10 +162,4 @@ body.welcome-page {
font-size: 32px;
}
}
.welcome-watermark {
position: absolute;
width: 100%;
height: 100%;
}
}

View File

@@ -126,6 +126,7 @@ case "$1" in
fi
PR11_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'prosody-0.11' 2>/dev/null | awk '{print $3}' || true)"
PR10_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'prosody-0.10' 2>/dev/null | awk '{print $3}' || true)"
PR_VER_INSTALLED=$(dpkg-query -f='${Version}\n' --show prosody 2>/dev/null || true)
if [ "$PR11_INSTALL_CHECK" = "installed" ] \
|| [ "$PR11_INSTALL_CHECK" = "unpacked" ] \
@@ -137,6 +138,19 @@ case "$1" in
PROSODY_CONFIG_PRESENT="false"
fi
fi
if [ "$PR10_INSTALL_CHECK" = "installed" ] \
|| [ "$PR10_INSTALL_CHECK" = "unpacked" ] \
|| dpkg --compare-versions "$PR_VER_INSTALLED" gt "0.10" ; then
# if the version is 0.10.X (>0.10 and <0.11)
if [ -f $PROSODY_HOST_CONFIG ] \
&& dpkg --compare-versions "$PR_VER_INSTALLED" lt "0.11" ; then
sed -i 's/storage = \"null\"/storage = \"none\"/g' $PROSODY_HOST_CONFIG
# trigger a restart
PROSODY_CONFIG_PRESENT="false"
fi
fi
if [ ! -f /var/lib/prosody/$JICOFO_AUTH_DOMAIN.crt ]; then
# prosodyctl takes care for the permissions

View File

@@ -236,19 +236,12 @@ invoke-rc.d nginx restart
```
## Running behind NAT
Jitsi-Videobridge can run behind a NAT, provided that all required ports are routed (forwarded) to the machine that it runs on. By default these ports are (TCP/443 or TCP/4443 and UDP 10000-20000).
Jitsi-Videobridge can run behind a NAT, provided that all required ports are routed (forwarded) to the machine that it runs on. By default these ports are (TCP/443 or TCP/4443 and UDP 10000).
The following extra lines need to be added the file `~/.sip-communicator/sip-communicator.properties` (in the home directory of the user running the videobridge):
```
org.jitsi.videobridge.NAT_HARVESTER_LOCAL_ADDRESS=<Local.IP.Address>
org.jitsi.videobridge.NAT_HARVESTER_PUBLIC_ADDRESS=<Public.IP.Address>
```
So the file should look like this at the end:
```
org.jitsi.impl.neomedia.transform.srtp.SRTPCryptoContext.checkReplay=false
org.jitsi.videobridge.NAT_HARVESTER_LOCAL_ADDRESS=<Local.IP.Address>
org.jitsi.videobridge.NAT_HARVESTER_PUBLIC_ADDRESS=<Public.IP.Address>
org.ice4j.ice.harvest.NAT_HARVESTER_LOCAL_ADDRESS=<Local.IP.Address>
org.ice4j.ice.harvest.NAT_HARVESTER_PUBLIC_ADDRESS=<Public.IP.Address>
```
# Hold your first conference

View File

@@ -51,6 +51,7 @@ target 'JitsiMeet' do
: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
@@ -58,7 +59,7 @@ end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
config.build_settings['ENABLE_BITCODE'] = 'YES'
end
end
end

View File

@@ -84,8 +84,8 @@ PODS:
- nanopb/decode (0.3.901)
- nanopb/encode (0.3.901)
- ObjectiveDropboxOfficial (3.9.4)
- React (0.59.4):
- React/Core (= 0.59.4)
- React (0.59.5):
- React/Core (= 0.59.5)
- react-native-background-timer (2.1.1):
- React
- react-native-calendar-events (1.6.4):
@@ -97,52 +97,52 @@ PODS:
- SDWebImage/GIF
- react-native-keep-awake (4.0.0):
- React
- react-native-webrtc (1.69.0):
- react-native-webrtc (1.69.1):
- React
- React/Core (0.59.4):
- yoga (= 0.59.4.React)
- React/CxxBridge (0.59.4):
- 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.4):
- 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.4):
- React/DevSupport (0.59.5):
- React/Core
- React/RCTWebSocket
- React/fishhook (0.59.4)
- React/jsi (0.59.4):
- React/fishhook (0.59.5)
- React/jsi (0.59.5):
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React/jsiexecutor (0.59.4):
- React/jsiexecutor (0.59.5):
- DoubleConversion
- Folly (= 2018.10.22.00)
- glog
- React/cxxreact
- React/jsi
- React/jsinspector (0.59.4)
- React/RCTActionSheet (0.59.4):
- React/jsinspector (0.59.5)
- React/RCTActionSheet (0.59.5):
- React/Core
- React/RCTAnimation (0.59.4):
- React/RCTAnimation (0.59.5):
- React/Core
- React/RCTBlob (0.59.4):
- React/RCTBlob (0.59.5):
- React/Core
- React/RCTImage (0.59.4):
- React/RCTImage (0.59.5):
- React/Core
- React/RCTNetwork
- React/RCTLinkingIOS (0.59.4):
- React/RCTLinkingIOS (0.59.5):
- React/Core
- React/RCTNetwork (0.59.4):
- React/RCTNetwork (0.59.5):
- React/Core
- React/RCTText (0.59.4):
- React/RCTText (0.59.5):
- React/Core
- React/RCTWebSocket (0.59.4):
- React/RCTWebSocket (0.59.5):
- React/Core
- React/fishhook
- React/RCTBlob
@@ -156,11 +156,13 @@ PODS:
- React/Core
- RNVectorIcons (6.0.2):
- React
- RNWatch (0.2.0):
- React
- SDWebImage/Core (4.4.6)
- SDWebImage/GIF (4.4.6):
- FLAnimatedImage (~> 1.0)
- SDWebImage/Core
- yoga (0.59.4.React)
- yoga (0.59.5.React)
DEPENDENCIES:
- Amplitude-iOS (~> 4.0.4)
@@ -191,6 +193,7 @@ DEPENDENCIES:
- RNGoogleSignin (from `../node_modules/react-native-google-signin`)
- RNSound (from `../node_modules/react-native-sound`)
- RNVectorIcons (from `../node_modules/react-native-vector-icons`)
- RNWatch (from `../node_modules/react-native-watch-connectivity`)
- yoga (from `../node_modules/react-native/ReactCommon/yoga`)
SPEC REPOS:
@@ -242,6 +245,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native-sound"
RNVectorIcons:
:path: "../node_modules/react-native-vector-icons"
RNWatch:
:path: "../node_modules/react-native-watch-connectivity"
yoga:
:path: "../node_modules/react-native/ReactCommon/yoga"
@@ -268,18 +273,19 @@ SPEC CHECKSUMS:
GTMSessionFetcher: 32aeca0aa144acea523e1c8e053089dec2cb98ca
nanopb: 2901f78ea1b7b4015c860c2fdd1ea2fee1a18d48
ObjectiveDropboxOfficial: a5afefc83f6467c42c45f2253f583f2ad1ffc701
React: 5cb71fb1a15b5ce04794ab49e24b48ebe4c94e65
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: a14197fefe96ab462dc098b79c428fc5a7f68216
react-native-webrtc: 90a847d19deb2d7323fef8cc89ca12b8995fbc90
RNGoogleSignin: 361174d9a3090d295b06257162b560d8efc8a6ed
RNSound: e157320f503bdd4f4ee6d8542e948d54f90c3c3a
RNVectorIcons: d819334932bcda3332deb3d2c8ea4d069e0b98f9
RNWatch: 09738b339eceb66e4d80a2371633ca5fb380fa42
SDWebImage: 3f3f0c02f09798048c47a5ed0a13f17b063572d8
yoga: 596e61c9b57751d08a22b07aba310dbd3e65ab75
yoga: 2e571f113e8cbeb0eb752aeebc86c1bfe7a8200c
PODFILE CHECKSUM: 4a11c3d66127a9845d4a5b2c7fad49f58a9c7a89
PODFILE CHECKSUM: 9e6bc935ea7d2974604572cc68938281a88cf35c
COCOAPODS: 1.6.1

View File

@@ -4,6 +4,7 @@
<dict>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:alpha.jitsi.net</string>
<string>applinks:beta.meet.jit.si</string>
<string>applinks:meet.jit.si</string>
</array>

View File

@@ -11,16 +11,45 @@
0B26BE6F1EC5BC3C00EEFB41 /* JitsiMeet.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0B26BE6D1EC5BC3C00EEFB41 /* JitsiMeet.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
0B412F1F1EDEE6E800B1A0A6 /* ViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 0B412F1E1EDEE6E800B1A0A6 /* ViewController.m */; };
0B412F211EDEE95300B1A0A6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0B412F201EDEE95300B1A0A6 /* Main.storyboard */; };
0B5418471F7C5D8C00A2DD86 /* MeetingRowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B5418461F7C5D8C00A2DD86 /* MeetingRowController.swift */; };
0B7001701F7C51CC005944F4 /* InCallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0B70016F1F7C51CC005944F4 /* InCallController.swift */; };
0BD6B4371EF82A6B00D1F4CD /* WebRTC.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BD6B4361EF82A6B00D1F4CD /* WebRTC.framework */; };
0BD6B4381EF82A6B00D1F4CD /* WebRTC.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 0BD6B4361EF82A6B00D1F4CD /* WebRTC.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
0BEA5C291F7B8F73000D0AB4 /* Interface.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0BEA5C271F7B8F73000D0AB4 /* Interface.storyboard */; };
0BEA5C2B1F7B8F73000D0AB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0BEA5C2A1F7B8F73000D0AB4 /* Assets.xcassets */; };
0BEA5C321F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 0BEA5C311F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
0BEA5C371F7B8F73000D0AB4 /* InterfaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BEA5C361F7B8F73000D0AB4 /* InterfaceController.swift */; };
0BEA5C391F7B8F73000D0AB4 /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BEA5C381F7B8F73000D0AB4 /* ExtensionDelegate.swift */; };
0BEA5C3B1F7B8F73000D0AB4 /* ComplicationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BEA5C3A1F7B8F73000D0AB4 /* ComplicationController.swift */; };
0BEA5C3D1F7B8F73000D0AB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0BEA5C3C1F7B8F73000D0AB4 /* Assets.xcassets */; };
0BEA5C411F7B8F73000D0AB4 /* JitsiMeetCompanion.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 0BEA5C251F7B8F73000D0AB4 /* JitsiMeetCompanion.app */; };
13B07FBC1A68108700A75B9A /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.m */; };
13B07FBD1A68108700A75B9A /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB11A68108700A75B9A /* LaunchScreen.xib */; };
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
695AF3ED6F686F9C5EE40F9A /* libPods-jitsi-meet.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 489E8EFE2C720D10F5961AEF /* libPods-jitsi-meet.a */; };
DE4C456121DE1E4E00EA0709 /* FIRUtilities.m in Sources */ = {isa = PBXBuildFile; fileRef = DE4C455F21DE1E4E00EA0709 /* FIRUtilities.m */; };
E588011722789D43008B0561 /* JitsiMeetContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = E58801132278944E008B0561 /* JitsiMeetContext.swift */; };
E5C97B63227A1EB400199214 /* JitsiMeetCommands.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5C97B62227A1EB400199214 /* JitsiMeetCommands.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
0BEA5C331F7B8F73000D0AB4 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 0BEA5C301F7B8F73000D0AB4;
remoteInfo = "JitsiMeetCompanion Extension";
};
0BEA5C3F1F7B8F73000D0AB4 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 0BEA5C241F7B8F73000D0AB4;
remoteInfo = JitsiMeetCompanion;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
0B26BE701EC5BC3C00EEFB41 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
@@ -34,6 +63,28 @@
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
0BEA5C471F7B8F73000D0AB4 /* Embed App Extensions */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 13;
files = (
0BEA5C321F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex in Embed App Extensions */,
);
name = "Embed App Extensions";
runOnlyForDeploymentPostprocessing = 0;
};
0BEA5C491F7B8F73000D0AB4 /* Embed Watch Content */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "$(CONTENTS_FOLDER_PATH)/Watch";
dstSubfolderSpec = 16;
files = (
0BEA5C411F7B8F73000D0AB4 /* JitsiMeetCompanion.app in Embed Watch Content */,
);
name = "Embed Watch Content";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
@@ -42,8 +93,20 @@
0B412F1D1EDEE6E800B1A0A6 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
0B412F1E1EDEE6E800B1A0A6 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
0B412F201EDEE95300B1A0A6 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
0B5418461F7C5D8C00A2DD86 /* MeetingRowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeetingRowController.swift; sourceTree = "<group>"; };
0B70016F1F7C51CC005944F4 /* InCallController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InCallController.swift; sourceTree = "<group>"; };
0BBD021F212EB69D00CCB19F /* Types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Types.h; sourceTree = "<group>"; };
0BD6B4361EF82A6B00D1F4CD /* WebRTC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebRTC.framework; path = "../../node_modules/react-native-webrtc/ios/WebRTC.framework"; sourceTree = "<group>"; };
0BEA5C251F7B8F73000D0AB4 /* JitsiMeetCompanion.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JitsiMeetCompanion.app; sourceTree = BUILT_PRODUCTS_DIR; };
0BEA5C281F7B8F73000D0AB4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Interface.storyboard; sourceTree = "<group>"; };
0BEA5C2A1F7B8F73000D0AB4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
0BEA5C2C1F7B8F73000D0AB4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
0BEA5C311F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "JitsiMeetCompanion Extension.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
0BEA5C361F7B8F73000D0AB4 /* InterfaceController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InterfaceController.swift; sourceTree = "<group>"; };
0BEA5C381F7B8F73000D0AB4 /* ExtensionDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtensionDelegate.swift; sourceTree = "<group>"; };
0BEA5C3A1F7B8F73000D0AB4 /* ComplicationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComplicationController.swift; sourceTree = "<group>"; };
0BEA5C3C1F7B8F73000D0AB4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
0BEA5C3E1F7B8F73000D0AB4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* jitsi-meet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "jitsi-meet.app"; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
13B07FB01A68108700A75B9A /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
@@ -56,9 +119,18 @@
B3B083EB1D4955FF0069CEE7 /* app.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = app.entitlements; sourceTree = "<group>"; };
DE4C455F21DE1E4E00EA0709 /* FIRUtilities.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FIRUtilities.m; sourceTree = "<group>"; };
DE4C456021DE1E4E00EA0709 /* FIRUtilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FIRUtilities.h; sourceTree = "<group>"; };
E58801132278944E008B0561 /* JitsiMeetContext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JitsiMeetContext.swift; sourceTree = "<group>"; };
E5C97B62227A1EB400199214 /* JitsiMeetCommands.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JitsiMeetCommands.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
0BEA5C2E1F7B8F73000D0AB4 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -69,6 +141,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
1F021A8A5B056078665DE530 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
@@ -82,6 +161,34 @@
name = Frameworks;
sourceTree = "<group>";
};
0BEA5C261F7B8F73000D0AB4 /* Watch app */ = {
isa = PBXGroup;
children = (
0BEA5C271F7B8F73000D0AB4 /* Interface.storyboard */,
0BEA5C2A1F7B8F73000D0AB4 /* Assets.xcassets */,
0BEA5C2C1F7B8F73000D0AB4 /* Info.plist */,
);
name = "Watch app";
path = watchos/app;
sourceTree = "<group>";
};
0BEA5C351F7B8F73000D0AB4 /* WatchKit extension */ = {
isa = PBXGroup;
children = (
0BEA5C361F7B8F73000D0AB4 /* InterfaceController.swift */,
0BEA5C381F7B8F73000D0AB4 /* ExtensionDelegate.swift */,
0BEA5C3A1F7B8F73000D0AB4 /* ComplicationController.swift */,
0BEA5C3C1F7B8F73000D0AB4 /* Assets.xcassets */,
0BEA5C3E1F7B8F73000D0AB4 /* Info.plist */,
0B70016F1F7C51CC005944F4 /* InCallController.swift */,
0B5418461F7C5D8C00A2DD86 /* MeetingRowController.swift */,
E58801132278944E008B0561 /* JitsiMeetContext.swift */,
E5C97B62227A1EB400199214 /* JitsiMeetCommands.swift */,
);
name = "WatchKit extension";
path = watchos/extension;
sourceTree = "<group>";
};
13B07FAE1A68108700A75B9A /* src */ = {
isa = PBXGroup;
children = (
@@ -118,6 +225,8 @@
83CBBA001A601CBA00E9B192 /* Products */,
13B07FAE1A68108700A75B9A /* src */,
5E96ADD5E49F3B3822EF9A52 /* Pods */,
0BEA5C261F7B8F73000D0AB4 /* Watch app */,
0BEA5C351F7B8F73000D0AB4 /* WatchKit extension */,
);
indentWidth = 2;
sourceTree = "<group>";
@@ -127,6 +236,8 @@
isa = PBXGroup;
children = (
13B07F961A680F5B00A75B9A /* jitsi-meet.app */,
0BEA5C251F7B8F73000D0AB4 /* JitsiMeetCompanion.app */,
0BEA5C311F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex */,
);
name = Products;
sourceTree = "<group>";
@@ -134,6 +245,41 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
0BEA5C241F7B8F73000D0AB4 /* JitsiMeetCompanion */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0BEA5C481F7B8F73000D0AB4 /* Build configuration list for PBXNativeTarget "JitsiMeetCompanion" */;
buildPhases = (
0BEA5C231F7B8F73000D0AB4 /* Resources */,
0BEA5C471F7B8F73000D0AB4 /* Embed App Extensions */,
1F021A8A5B056078665DE530 /* Frameworks */,
);
buildRules = (
);
dependencies = (
0BEA5C341F7B8F73000D0AB4 /* PBXTargetDependency */,
);
name = JitsiMeetCompanion;
productName = JitsiMeetCompanion;
productReference = 0BEA5C251F7B8F73000D0AB4 /* JitsiMeetCompanion.app */;
productType = "com.apple.product-type.application.watchapp2";
};
0BEA5C301F7B8F73000D0AB4 /* JitsiMeetCompanion Extension */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0BEA5C461F7B8F73000D0AB4 /* Build configuration list for PBXNativeTarget "JitsiMeetCompanion Extension" */;
buildPhases = (
0BEA5C2D1F7B8F73000D0AB4 /* Sources */,
0BEA5C2E1F7B8F73000D0AB4 /* Frameworks */,
0BEA5C2F1F7B8F73000D0AB4 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = "JitsiMeetCompanion Extension";
productName = "JitsiMeetCompanion Extension";
productReference = 0BEA5C311F7B8F73000D0AB4 /* JitsiMeetCompanion Extension.appex */;
productType = "com.apple.product-type.watchkit2-extension";
};
13B07F861A680F5B00A75B9A /* jitsi-meet */ = {
isa = PBXNativeTarget;
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "jitsi-meet" */;
@@ -150,10 +296,12 @@
DEC2069321CBBD6900072F03 /* Setup Fabric */,
DE11877A21EE09640078D059 /* Setup Google reverse URL handler */,
DE4F6D6E22005C0400DE699E /* Setup Dropbox */,
0BEA5C491F7B8F73000D0AB4 /* Embed Watch Content */,
);
buildRules = (
);
dependencies = (
0BEA5C401F7B8F73000D0AB4 /* PBXTargetDependency */,
);
name = "jitsi-meet";
productName = "Jitsi Meet";
@@ -169,6 +317,16 @@
LastUpgradeCheck = 1020;
ORGANIZATIONNAME = Facebook;
TargetAttributes = {
0BEA5C241F7B8F73000D0AB4 = {
CreatedOnToolsVersion = 9.0;
DevelopmentTeam = FC967L3QRG;
ProvisioningStyle = Automatic;
};
0BEA5C301F7B8F73000D0AB4 = {
CreatedOnToolsVersion = 9.0;
DevelopmentTeam = FC967L3QRG;
ProvisioningStyle = Automatic;
};
13B07F861A680F5B00A75B9A = {
DevelopmentTeam = FC967L3QRG;
ProvisioningStyle = Automatic;
@@ -197,11 +355,30 @@
projectRoot = "";
targets = (
13B07F861A680F5B00A75B9A /* jitsi-meet */,
0BEA5C241F7B8F73000D0AB4 /* JitsiMeetCompanion */,
0BEA5C301F7B8F73000D0AB4 /* JitsiMeetCompanion Extension */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
0BEA5C231F7B8F73000D0AB4 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0BEA5C2B1F7B8F73000D0AB4 /* Assets.xcassets in Resources */,
0BEA5C291F7B8F73000D0AB4 /* Interface.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
0BEA5C2F1F7B8F73000D0AB4 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0BEA5C3D1F7B8F73000D0AB4 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
13B07F8E1A680F5B00A75B9A /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -354,6 +531,20 @@
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
0BEA5C2D1F7B8F73000D0AB4 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
0B7001701F7C51CC005944F4 /* InCallController.swift in Sources */,
E5C97B63227A1EB400199214 /* JitsiMeetCommands.swift in Sources */,
0B5418471F7C5D8C00A2DD86 /* MeetingRowController.swift in Sources */,
E588011722789D43008B0561 /* JitsiMeetContext.swift in Sources */,
0BEA5C391F7B8F73000D0AB4 /* ExtensionDelegate.swift in Sources */,
0BEA5C371F7B8F73000D0AB4 /* InterfaceController.swift in Sources */,
0BEA5C3B1F7B8F73000D0AB4 /* ComplicationController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
13B07F871A680F5B00A75B9A /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -367,7 +558,28 @@
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
0BEA5C341F7B8F73000D0AB4 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 0BEA5C301F7B8F73000D0AB4 /* JitsiMeetCompanion Extension */;
targetProxy = 0BEA5C331F7B8F73000D0AB4 /* PBXContainerItemProxy */;
};
0BEA5C401F7B8F73000D0AB4 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 0BEA5C241F7B8F73000D0AB4 /* JitsiMeetCompanion */;
targetProxy = 0BEA5C3F1F7B8F73000D0AB4 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
0BEA5C271F7B8F73000D0AB4 /* Interface.storyboard */ = {
isa = PBXVariantGroup;
children = (
0BEA5C281F7B8F73000D0AB4 /* Base */,
);
name = Interface.storyboard;
sourceTree = "<group>";
};
13B07FB11A68108700A75B9A /* LaunchScreen.xib */ = {
isa = PBXVariantGroup;
children = (
@@ -379,6 +591,140 @@
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
0BEA5C421F7B8F73000D0AB4 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = FC967L3QRG;
GCC_C_LANGUAGE_STANDARD = gnu11;
IBSC_MODULE = JitsiMeetCompanion_Extension;
INFOPLIST_FILE = watchos/app/Info.plist;
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet.watchkit;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = watchos;
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 4.0;
};
name = Debug;
};
0BEA5C431F7B8F73000D0AB4 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = FC967L3QRG;
GCC_C_LANGUAGE_STANDARD = gnu11;
IBSC_MODULE = JitsiMeetCompanion_Extension;
INFOPLIST_FILE = watchos/app/Info.plist;
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet.watchkit;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = watchos;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 4.0;
};
name = Release;
};
0BEA5C441F7B8F73000D0AB4 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
DEBUG_INFORMATION_FORMAT = dwarf;
DEVELOPMENT_TEAM = FC967L3QRG;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = watchos/extension/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet.watchkit.extension;
PRODUCT_NAME = "${TARGET_NAME}";
SDKROOT = watchos;
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 4.0;
};
name = Debug;
};
0BEA5C451F7B8F73000D0AB4 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_COMPLICATION_NAME = Complication;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEVELOPMENT_TEAM = FC967L3QRG;
GCC_C_LANGUAGE_STANDARD = gnu11;
INFOPLIST_FILE = watchos/extension/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = org.jitsi.meet.watchkit.extension;
PRODUCT_NAME = "${TARGET_NAME}";
SDKROOT = watchos;
SKIP_INSTALL = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 4.0;
};
name = Release;
};
13B07F941A680F5B00A75B9A /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 4670A512A688E2DC34528282 /* Pods-jitsi-meet.debug.xcconfig */;
@@ -391,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",
@@ -427,7 +773,7 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = FC967L3QRG;
ENABLE_BITCODE = NO;
ENABLE_BITCODE = YES;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"../../node_modules/react-native-webrtc/ios",
@@ -482,7 +828,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
ENABLE_BITCODE = NO;
ENABLE_BITCODE = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
@@ -504,7 +850,7 @@
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
);
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -541,7 +887,7 @@
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = YES;
ENABLE_BITCODE = NO;
ENABLE_BITCODE = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
@@ -556,9 +902,10 @@
"$(inherited)",
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include,
);
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
VALIDATE_PRODUCT = YES;
};
name = Release;
@@ -566,6 +913,24 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
0BEA5C461F7B8F73000D0AB4 /* Build configuration list for PBXNativeTarget "JitsiMeetCompanion Extension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0BEA5C441F7B8F73000D0AB4 /* Debug */,
0BEA5C451F7B8F73000D0AB4 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
0BEA5C481F7B8F73000D0AB4 /* Build configuration list for PBXNativeTarget "JitsiMeetCompanion" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0BEA5C421F7B8F73000D0AB4 /* Debug */,
0BEA5C431F7B8F73000D0AB4 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "jitsi-meet" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View File

@@ -41,7 +41,7 @@
jitsiMeet.conferenceActivityType = JitsiMeetConferenceActivityType;
jitsiMeet.customUrlScheme = @"org.jitsi.meet";
jitsiMeet.universalLinkDomains = @[@"meet.jit.si", @"beta.meet.jit.si"];
jitsiMeet.universalLinkDomains = @[@"meet.jit.si", @"alpha.jitsi.net", @"beta.meet.jit.si"];
jitsiMeet.defaultConferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
builder.serverURL = [NSURL URLWithString:@"https://meet.jit.si"];
@@ -67,9 +67,9 @@
= [[FIRDynamicLinks dynamicLinks]
handleUniversalLink:userActivity.webpageURL
completion:^(FIRDynamicLink * _Nullable dynamicLink, NSError * _Nullable error) {
NSURL *dynamicLinkURL = dynamicLink.url;
if (dynamicLinkURL) {
userActivity.webpageURL = dynamicLinkURL;
NSURL *firebaseUrl = [FIRUtilities extractURL:dynamicLink];
if (firebaseUrl != nil) {
userActivity.webpageURL = firebaseUrl;
[[JitsiMeet sharedInstance] application:application
continueUserActivity:userActivity
restorationHandler:restorationHandler];
@@ -91,19 +91,20 @@
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
// This shows up during a reload in development, skip it.
// https://github.com/firebase/firebase-ios-sdk/issues/233
if ([[url absoluteString] containsString:@"google/link/?dismiss=1&is_weak_match=1"]) {
return NO;
}
NSURL *openUrl = url;
if ([FIRUtilities appContainsRealServiceInfoPlist]) {
// Process Firebase Dynamic Links
FIRDynamicLink *dynamicLink = [[FIRDynamicLinks dynamicLinks] dynamicLinkFromCustomSchemeURL:url];
if (dynamicLink != nil) {
NSURL *dynamicLinkURL = dynamicLink.url;
if (dynamicLinkURL != nil
&& (dynamicLink.matchType == FIRDLMatchTypeUnique
|| dynamicLink.matchType == FIRDLMatchTypeDefault)) {
// Strong match, process it.
openUrl = dynamicLinkURL;
}
NSURL *firebaseUrl = [FIRUtilities extractURL:dynamicLink];
if (firebaseUrl != nil) {
openUrl = firebaseUrl;
}
}

View File

@@ -16,8 +16,12 @@
#import <Foundation/Foundation.h>
@import Firebase;
@interface FIRUtilities : NSObject
+ (BOOL)appContainsRealServiceInfoPlist;
+ (NSURL *_Nullable)extractURL: (FIRDynamicLink* _Nullable)dynamicLink;
@end

View File

@@ -61,4 +61,19 @@ NSString *const kGoogleAppIDPlistKey = @"GOOGLE_APP_ID";
return YES;
}
+ (NSURL *)extractURL: (FIRDynamicLink*)dynamicLink {
NSURL *url = nil;
if (dynamicLink != nil) {
NSURL *dynamicLinkURL = dynamicLink.url;
if (dynamicLinkURL != nil
&& (dynamicLink.matchType == FIRDLMatchTypeUnique
|| dynamicLink.matchType == FIRDLMatchTypeDefault)) {
// Strong match, process it.
url = dynamicLinkURL;
}
}
return url;
}
@end

View File

@@ -0,0 +1,92 @@
{
"images" : [
{
"size" : "24x24",
"idiom" : "watch",
"filename" : "Icon-24@2x.png",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "38mm"
},
{
"size" : "27.5x27.5",
"idiom" : "watch",
"filename" : "Icon-27.5@2x.png",
"scale" : "2x",
"role" : "notificationCenter",
"subtype" : "42mm"
},
{
"size" : "29x29",
"idiom" : "watch",
"filename" : "Icon-29@2x.png",
"role" : "companionSettings",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "watch",
"filename" : "Icon-29@3x.png",
"role" : "companionSettings",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "watch",
"filename" : "Icon-40@2x.png",
"scale" : "2x",
"role" : "appLauncher",
"subtype" : "38mm"
},
{
"size" : "44x44",
"idiom" : "watch",
"filename" : "Icon-88@2x.png",
"scale" : "2x",
"role" : "appLauncher",
"subtype" : "40mm"
},
{
"size" : "50x50",
"idiom" : "watch",
"filename" : "Icon-100@2x.png",
"scale" : "2x",
"role" : "appLauncher",
"subtype" : "44mm"
},
{
"size" : "86x86",
"idiom" : "watch",
"filename" : "Icon-86@2x.png",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "38mm"
},
{
"size" : "98x98",
"idiom" : "watch",
"filename" : "Icon-98@2x.png",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "42mm"
},
{
"size" : "108x108",
"idiom" : "watch",
"filename" : "Icon-216@2x.png",
"scale" : "2x",
"role" : "quickLook",
"subtype" : "44mm"
},
{
"size" : "1024x1024",
"idiom" : "watch-marketing",
"filename" : "Icon-1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "hangup@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.5 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "mute-off@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "mute-on@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@@ -0,0 +1,83 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder.WatchKit.Storyboard" version="3.0" toolsVersion="14490.70" targetRuntime="watchKit" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="AgC-eL-Hgc">
<device id="watch38" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="watchOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBWatchKitPlugin" version="14490.21"/>
</dependencies>
<scenes>
<!--Meetings-->
<scene sceneID="aou-V4-d1y">
<objects>
<controller title="Meetings" id="AgC-eL-Hgc" customClass="InterfaceController" customModule="JitsiMeetCompanion" customModuleProvider="target">
<items>
<table alignment="left" id="gpO-ql-Xsr">
<items>
<tableRow identifier="MeetingRowType" id="GGl-av-xeJ" customClass="MeetingRowController" customModule="JitsiMeetCompanion_Extension">
<group key="rootItem" width="1" height="0.0" alignment="left" layout="vertical" id="5XE-gq-qzG">
<items>
<label alignment="left" text="Label" id="Sij-up-N4p"/>
<label alignment="left" text="Label" id="V5K-sm-jEH">
<color key="textColor" white="0.66666666666666663" alpha="1" colorSpace="calibratedWhite"/>
<fontDescription key="font" style="UICTFontTextStyleFootnote"/>
</label>
</items>
<connections>
<segue destination="9RD-qP-1Z0" kind="push" id="Boa-6E-eZs"/>
</connections>
</group>
<connections>
<outlet property="roomLabel" destination="Sij-up-N4p" id="PdS-SO-ylc"/>
<outlet property="rowGroup" destination="5XE-gq-qzG" id="GZN-2c-2Gz"/>
<outlet property="timeLabel" destination="V5K-sm-jEH" id="fWQ-kx-vE4"/>
</connections>
</tableRow>
</items>
</table>
</items>
<connections>
<outlet property="table" destination="gpO-ql-Xsr" id="aVV-iZ-z3l"/>
</connections>
</controller>
</objects>
<point key="canvasLocation" x="-99" y="117"/>
</scene>
<!--Meetings-->
<scene sceneID="ns4-Kh-qqU">
<objects>
<controller identifier="InCallController" title="Meetings" hidesWhenLoading="NO" id="9RD-qP-1Z0" customClass="InCallController" customModule="JitsiMeetCompanion" customModuleProvider="target">
<items>
<label alignment="center" text="Label" id="vFt-lL-SNY"/>
<timer alignment="center" textAlignment="center" previewedSeconds="0" id="W8S-uZ-MPm">
<color key="textColor" red="0.024725984125768763" green="1" blue="0.24241188365329402" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<fontDescription key="font" style="UICTFontTextStyleHeadline"/>
</timer>
<group alignment="center" verticalAlignment="bottom" spacing="10" id="Hfk-a0-uWj">
<items>
<button width="60" height="60" alignment="left" verticalAlignment="bottom" backgroundImage="hangup" id="8jF-SI-UHz">
<connections>
<action selector="hangupClicked" destination="9RD-qP-1Z0" id="cXK-lw-tsd"/>
</connections>
</button>
<button width="60" height="60" alignment="right" verticalAlignment="bottom" backgroundImage="mute-off" id="LmN-FI-aQq">
<connections>
<action selector="muteClicked" destination="9RD-qP-1Z0" id="dJg-kV-cqH"/>
</connections>
</button>
</items>
</group>
</items>
<connections>
<outlet property="mutedButton" destination="LmN-FI-aQq" id="gfi-4T-gdN"/>
<outlet property="roomLabel" destination="vFt-lL-SNY" id="cYB-Tf-Efz"/>
<outlet property="timer" destination="W8S-uZ-MPm" id="r7T-j1-9VJ"/>
</connections>
</controller>
</objects>
<point key="canvasLocation" x="213" y="117"/>
</scene>
</scenes>
</document>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Jitsi Meet</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>19.2.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
<key>WKCompanionAppBundleIdentifier</key>
<string>org.jitsi.meet</string>
<key>WKWatchKitApp</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}

View File

@@ -0,0 +1,21 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "jitsi@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -0,0 +1,81 @@
/*
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* 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 ClockKit
class ComplicationController: NSObject, CLKComplicationDataSource {
// MARK: - Timeline Configuration
func getSupportedTimeTravelDirections(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimeTravelDirections) -> Void) {
handler([])
}
func getPrivacyBehavior(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationPrivacyBehavior) -> Void) {
handler(.showOnLockScreen)
}
// MARK: - Timeline Population
func getCurrentTimelineEntry(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTimelineEntry?) -> Void) {
// Call the handler with the current timeline entry
getLocalizableSampleTemplate(for: complication) {template in
guard let template = template else {
handler(nil)
return
}
handler(CLKComplicationTimelineEntry(date: Date(), complicationTemplate: template))
}
}
func getTimelineEntries(for complication: CLKComplication, before date: Date, limit: Int, withHandler handler: @escaping ([CLKComplicationTimelineEntry]?) -> Void) {
// Call the handler with the timeline entries prior to the given date
handler(nil)
}
func getTimelineEntries(for complication: CLKComplication, after date: Date, limit: Int, withHandler handler: @escaping ([CLKComplicationTimelineEntry]?) -> Void) {
// Call the handler with the timeline entries after to the given date
handler(nil)
}
// MARK: - Placeholder Templates
func getLocalizableSampleTemplate(for complication: CLKComplication, withHandler handler: @escaping (CLKComplicationTemplate?) -> Void) {
// This method will be called once per supported complication, and the results will be cached
let imageProvider = CLKImageProvider(onePieceImage: UIImage(named: "jitsi")!)
if complication.family == .circularSmall {
let small = CLKComplicationTemplateCircularSmallRingImage()
small.imageProvider = imageProvider
small.ringStyle = .closed
small.fillFraction = 0
handler(small)
} else if complication.family == .utilitarianSmall {
let utilitarian = CLKComplicationTemplateUtilitarianSmallSquare()
utilitarian.imageProvider = imageProvider
handler(utilitarian)
} else if complication.family == .modularSmall {
let modular = CLKComplicationTemplateModularSmallRingImage()
modular.imageProvider = imageProvider
modular.ringStyle = .closed
modular.fillFraction = 0
handler(modular)
}
}
}

View File

@@ -0,0 +1,103 @@
/*
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* 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 WatchConnectivity
import WatchKit
class ExtensionDelegate: NSObject, WCSessionDelegate, WKExtensionDelegate {
var currentContext : JitsiMeetContext = JitsiMeetContext()
static var currentJitsiMeetContext: JitsiMeetContext {
get {
return (WKExtension.shared().delegate as! ExtensionDelegate).currentContext
}
}
func applicationDidFinishLaunching() {
// Start Watch Connectivity
if WCSession.isSupported() {
let session = WCSession.default
session.delegate = self
session.activate()
}
}
func handle(_ backgroundTasks: Set<WKRefreshBackgroundTask>) {
// Sent when the system needs to launch the application in the background to process tasks. Tasks arrive in a set, so loop through and process each one.
for task in backgroundTasks {
// Use a switch statement to check the task type
switch task {
case let backgroundTask as WKApplicationRefreshBackgroundTask:
// Be sure to complete the background task once you’re done.
backgroundTask.setTaskCompletedWithSnapshot(false)
case let snapshotTask as WKSnapshotRefreshBackgroundTask:
// Snapshot tasks have a unique completion call, make sure to set your expiration date
snapshotTask.setTaskCompleted(restoredDefaultState: true, estimatedSnapshotExpiration: Date.distantFuture, userInfo: nil)
case let connectivityTask as WKWatchConnectivityRefreshBackgroundTask:
// Be sure to complete the connectivity task once you’re done.
connectivityTask.setTaskCompletedWithSnapshot(false)
case let urlSessionTask as WKURLSessionRefreshBackgroundTask:
// Be sure to complete the URL session task once you’re done.
urlSessionTask.setTaskCompletedWithSnapshot(false)
default:
// make sure to complete unhandled task types
task.setTaskCompletedWithSnapshot(false)
}
}
}
func session(_ session: WCSession, activationDidCompleteWith
activationState: WCSessionActivationState, error: Error?) {
if let error = error {
print("WATCH Session activation failed with error: \(error.localizedDescription)")
return
}
print("WATCH Session activated with state: \(activationState.rawValue)")
}
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any]) {
DispatchQueue.main.async {
let newContext = JitsiMeetContext(context: applicationContext)
print("WATCH got new context: \(newContext.description)");
// Update context on the root controller which displays the recent list
let controller = WKExtension.shared().rootInterfaceController as! InterfaceController
controller.updateUI(newContext)
// If the current controller is not the in-call controller and we have a
// conference URL, show the in-call controller
if let currentController = WKExtension.shared().visibleInterfaceController as? InterfaceController {
// Go to the in-call controller only if the conference URL has changed, because the user may have
// clicked the back button
if newContext.conferenceURL != nil
&& self.currentContext.conferenceURL != newContext.conferenceURL {
currentController.pushController(withName: "InCallController", context: newContext)
}
} else if let inCallController = WKExtension.shared().visibleInterfaceController as? InCallController {
if newContext.conferenceURL == nil {
inCallController.popToRootController()
} else {
inCallController.updateUI(newContext)
}
}
self.currentContext = newContext;
}
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* 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 WatchConnectivity
import WatchKit
import Foundation
class InCallController: WKInterfaceController {
@IBOutlet var mutedButton: WKInterfaceButton!
@IBOutlet var roomLabel: WKInterfaceLabel!
@IBOutlet var timer: WKInterfaceTimer!
@IBAction func hangupClicked() {
sendCommand(JitsiMeetCommands.CMD_HANG_UP, message: nil)
}
@IBAction func muteClicked() {
if var micMuted = ExtensionDelegate.currentJitsiMeetContext.micMuted {
micMuted = !micMuted;
sendCommand(
JitsiMeetCommands.CMD_SET_MUTED,
message: [
"muted": micMuted ? "true" : "false"
])
updateMutedButton(withMuted: micMuted)
}
}
func sendCommand(_ command: JitsiMeetCommands, message: [String : Any]?) {
if WCSession.isSupported() {
let session = WCSession.default
var data = [String: Any]()
if let sessionID = ExtensionDelegate.currentJitsiMeetContext.sessionID {
if message != nil {
message!.forEach { data[$0] = $1 }
}
data["command"] = command.rawValue;
data["sessionID"] = sessionID;
session.sendMessage(data, replyHandler: nil, errorHandler: nil)
}
}
}
func updateUI(_ newContext: JitsiMeetContext) {
var conferenceURL = newContext.conferenceURL
if let joinConferenceURL = newContext.joinConferenceURL {
sendCommand(JitsiMeetCommands.CMD_JOIN_CONFERENCE, message: [ "data" : joinConferenceURL ])
conferenceURL = joinConferenceURL
}
let newRoomName = conferenceURL != nil ? conferenceURL!.components(separatedBy: "/").last : ""
roomLabel.setText(newRoomName)
if let newTimestamp = newContext.conferenceTimestamp {
restartTimer(newTimestamp)
}
if let newMuted = newContext.micMuted {
updateMutedButton(withMuted: newMuted)
}
}
func restartTimer(_ conferenceTimestamp: Int64) {
if (conferenceTimestamp != 0) {
let newDate = Date(timeIntervalSince1970: TimeInterval(conferenceTimestamp / 1000))
timer.setDate(newDate)
timer.start();
print("WATCH timer set date to: \(newDate) and start")
} else {
print("WATCH timer stop")
timer.stop();
}
}
func updateMutedButton(withMuted isMuted: Bool) {
if isMuted {
mutedButton.setBackgroundImageNamed("mute-on.png")
} else {
mutedButton.setBackgroundImageNamed("mute-off.png")
}
}
override func awake(withContext context: Any?) {
super.awake(withContext: context)
if let data = context as? JitsiMeetContext {
updateUI(data)
}
}
}

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>Jitsi Meet Companion Extension</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>XPC!</string>
<key>CFBundleShortVersionString</key>
<string>19.2.0</string>
<key>CFBundleVersion</key>
<string>1</string>
<key>CLKComplicationPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).ComplicationController</string>
<key>CLKComplicationSupportedFamilies</key>
<array>
<string>CLKComplicationFamilyModularSmall</string>
<string>CLKComplicationFamilyUtilitarianSmall</string>
<string>CLKComplicationFamilyCircularSmall</string>
</array>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>WKAppBundleIdentifier</key>
<string>org.jitsi.meet.watchkit</string>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.watchkit</string>
</dict>
<key>WKExtensionDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).ExtensionDelegate</string>
</dict>
</plist>

View File

@@ -0,0 +1,80 @@
/*
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* 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 WatchKit
import Foundation
class InterfaceController: WKInterfaceController {
@IBOutlet var table: WKInterfaceTable!
func updateUI(_ newContext:JitsiMeetContext) {
if let recentURLsArray = newContext.recentURLs {
updateRecents(withRecents: recentURLsArray, currentContext: newContext)
}
}
private func updateRecents(withRecents recents: NSArray, currentContext: JitsiMeetContext) {
// Updating the # of rows only if it actually changed prevents from blinking the UI
if (table.numberOfRows != recents.count) {
table.setNumberOfRows(recents.count, withRowType: "MeetingRowType")
}
for (index, entry) in recents.enumerated() {
let entryDict = entry as! NSDictionary
let roomURL = entryDict["conference"] as! NSString
let timestamp = entryDict["date"] as! NSNumber
// Prepare values
let room = roomURL.components(separatedBy: "/").last
let date = Date(timeIntervalSince1970: timestamp.doubleValue / 1000) // timestamp is taken with Date.now() in JS, which uses milliseconds
let dateFormatter = DateFormatter()
dateFormatter.timeZone = TimeZone.current
dateFormatter.locale = NSLocale.current
dateFormatter.dateFormat = "HH:mm yyyy-MM-dd"
let strDate = dateFormatter.string(from: date)
// Update row controller
let controller = table.rowController(at: index) as! MeetingRowController
controller.room = room
controller.roomUrl = roomURL as String
controller.roomLabel.setText(room)
controller.timeLabel.setText(strDate)
// Change the background for the active meeting
if (controller.roomUrl == currentContext.conferenceURL) {
controller.rowGroup.setBackgroundColor(UIColor(red: 0.125, green: 0.58, blue: 0.98, alpha: 1))
} else {
controller.rowGroup.setBackgroundColor(UIColor(red: 0.949, green: 0.956, blue: 1, alpha: 0.14))
}
}
}
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
print("WATCH contextForSegue: \(actionContext.description)");
return actionContext;
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* 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.
*/
// This needs to be in sync with features/mobile/watchos/constants.js
enum JitsiMeetCommands : String {
typealias RawValue = String
case CMD_HANG_UP = "hangup";
case CMD_JOIN_CONFERENCE = "joinConference";
case CMD_SET_MUTED = "setMuted";
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* 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
class JitsiMeetContext {
private var dictionary : [String : Any]
var joinConferenceURL : String? = nil;
init() {
dictionary = [:]
}
init(context: [String : Any]) {
dictionary = context
}
init(jmContext: JitsiMeetContext) {
dictionary = jmContext.dictionary
joinConferenceURL = jmContext.joinConferenceURL
}
var conferenceURL : String? {
get {
return dictionary["conferenceURL"] as? String
}
}
var conferenceTimestamp : Int64? {
get {
return dictionary["conferenceTimestamp"] as? Int64;
}
}
var sessionID : Int64? {
get {
return dictionary["sessionID"] as? Int64;
}
}
var recentURLs : NSArray? {
get {
return dictionary["recentURLs"] as? NSArray
}
}
var micMuted : Bool? {
get {
return (dictionary["micMuted"] as? NSNumber)?.boolValue ?? nil;
}
}
public var description: String {
return "JitsiMeetContext[conferenceURL: \(String(describing: conferenceURL)), conferenceTimestamp: \(String(describing:conferenceTimestamp)), sessionID: \(String(describing:sessionID)), recentURLs: \(String(describing:recentURLs)), joinConferenceURL: \(String(describing:joinConferenceURL)) "
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright @ 2018-present 8x8, Inc.
* Copyright @ 2017-2018 Atlassian Pty Ltd
*
* 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 WatchKit
class MeetingRowController: NSObject {
@IBOutlet var roomLabel: WKInterfaceLabel!
@IBOutlet var timeLabel: WKInterfaceLabel!
@IBOutlet var rowGroup: WKInterfaceGroup!
var room: String!
var roomUrl: String!
}

View File

@@ -16,6 +16,36 @@ platform :ios do
app_identifier: "com.atlassian.JitsiMeet.ios"
)
# Set the (watch) app identifier
update_app_identifier(
xcodeproj: "app/app.xcodeproj",
plist_path: "watchos/app/Info.plist",
app_identifier: "com.atlassian.JitsiMeet.ios.watchkit"
)
# Set the (watch) extension identifier
update_app_identifier(
xcodeproj: "app/app.xcodeproj",
plist_path: "watchos/extension/Info.plist",
app_identifier: "com.atlassian.JitsiMeet.ios.watchkit.extension"
)
update_info_plist(
xcodeproj: "app/app.xcodeproj",
plist_path: "watchos/app/Info.plist",
block: proc do |plist|
plist["WKCompanionAppBundleIdentifier"] = "com.atlassian.JitsiMeet.ios"
end
)
update_info_plist(
xcodeproj: "app/app.xcodeproj",
plist_path: "watchos/extension/Info.plist",
block: proc do |plist|
plist["NSExtension"]["NSExtensionAttributes"]["WKAppBundleIdentifier"] = "com.atlassian.JitsiMeet.ios.watchkit"
end
)
# Inrement the build number by 1
increment_build_number(
build_number: latest_testflight_build_number + 1,
@@ -25,7 +55,7 @@ platform :ios do
# Actually build the app
build_app(
scheme: "jitsi-meet",
include_bitcode: false,
include_bitcode: true,
include_symbols: true,
export_xcargs: "-allowProvisioningUpdates"
)

View File

@@ -518,7 +518,7 @@
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_BITCODE = NO;
ENABLE_BITCODE = YES;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
@@ -580,7 +580,7 @@
COPY_PHASE_STRIP = NO;
CURRENT_PROJECT_VERSION = 1;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_BITCODE = NO;
ENABLE_BITCODE = YES;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
@@ -615,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";
@@ -643,7 +643,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";

View File

@@ -20,51 +20,35 @@ import Foundation
internal final class JMCallKitEmitter: NSObject, CXProviderDelegate {
private var listeners = Set<JMCallKitEventListenerWrapper>()
private let listeners = NSMutableArray()
internal override init() {}
// MARK: - Add/remove listeners
func addListener(_ listener: JMCallKitListener) {
let wrapper = JMCallKitEventListenerWrapper(listener: listener)
listeners.insert(wrapper)
if (!listeners.contains(listener)) {
listeners.add(listener)
}
}
func removeListener(_ listener: JMCallKitListener) {
// XXX Constructing a new JMCallKitEventListenerWrapper instance in
// order to remove the specified listener from listeners is (1) a bit
// funny (though may make a statement about performance) and (2) not
// really an option because the specified listener may already be
// executing its dealloc (like RNCallKit).
listeners.forEach {
// 1. JMCallKitEventListenerWrapper weakly references
// JMCallKitListener so it may be nice to clean
// JMCallKitEventListenerWrapperinstances up if they've lost
// their associated JMCallKitListener instances (e.g. for
// example, because whoever did addListener forgot to
// removeListener). Unfortunately, I don't know how to do it
// because JMCallKitEventListenerWrapper is a struct.
//
// 2. XXX JMCallKitEventListenerWrapper implements the weird
// equality by JMCallKitListener hash which (1) I don't
// understand and (2) I don't know how to invoke without
// duplicating.
if ($0.hashValue == listener.hash) {
listeners.remove($0)
}
}
listeners.remove(listener)
}
// MARK: - CXProviderDelegate
func providerDidReset(_ provider: CXProvider) {
listeners.forEach { $0.listener?.providerDidReset?() }
listeners.forEach {
let listener = $0 as! JMCallKitListener
listener.providerDidReset?()
}
}
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
listeners.forEach {
$0.listener?.performAnswerCall?(UUID: action.callUUID)
let listener = $0 as! JMCallKitListener
listener.performAnswerCall?(UUID: action.callUUID)
}
action.fulfill()
@@ -72,7 +56,8 @@ internal final class JMCallKitEmitter: NSObject, CXProviderDelegate {
func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
listeners.forEach {
$0.listener?.performEndCall?(UUID: action.callUUID)
let listener = $0 as! JMCallKitListener
listener.performEndCall?(UUID: action.callUUID)
}
action.fulfill()
@@ -80,7 +65,8 @@ internal final class JMCallKitEmitter: NSObject, CXProviderDelegate {
func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
listeners.forEach {
$0.listener?.performSetMutedCall?(UUID: action.callUUID, isMuted: action.isMuted)
let listener = $0 as! JMCallKitListener
listener.performSetMutedCall?(UUID: action.callUUID, isMuted: action.isMuted)
}
action.fulfill()
@@ -88,8 +74,8 @@ internal final class JMCallKitEmitter: NSObject, CXProviderDelegate {
func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
listeners.forEach {
$0.listener?.performStartCall?(UUID: action.callUUID,
isVideo: action.isVideo)
let listener = $0 as! JMCallKitListener
listener.performStartCall?(UUID: action.callUUID, isVideo: action.isVideo)
}
action.fulfill()
@@ -98,35 +84,16 @@ internal final class JMCallKitEmitter: NSObject, CXProviderDelegate {
func provider(_ provider: CXProvider,
didActivate audioSession: AVAudioSession) {
listeners.forEach {
$0.listener?.providerDidActivateAudioSession?(session: audioSession)
let listener = $0 as! JMCallKitListener
listener.providerDidActivateAudioSession?(session: audioSession)
}
}
func provider(_ provider: CXProvider,
didDeactivate audioSession: AVAudioSession) {
listeners.forEach {
$0.listener?.providerDidDeactivateAudioSession?(
session: audioSession)
let listener = $0 as! JMCallKitListener
listener.providerDidDeactivateAudioSession?(session: audioSession)
}
}
}
fileprivate struct JMCallKitEventListenerWrapper: Hashable {
internal weak var listener: JMCallKitListener?
public init(listener: JMCallKitListener) {
self.listener = listener
}
public static func ==(lhs: JMCallKitEventListenerWrapper,
rhs: JMCallKitEventListenerWrapper) -> Bool {
// XXX We're aware that "[t]wo instances with equal hash values are not
// necessarily equal to each other."
return lhs.hashValue == rhs.hashValue
}
func hash(into hasher: inout Hasher) {
hasher.combine(self.listener?.hash)
}
}

View File

@@ -354,9 +354,10 @@
"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, dial __number__ and enter this PIN: __conferenceID__#",
"invitePhoneAlternatives": "To view more phone numbers, click this link: __url__",
"inviteURL": "To join the video meeting, click this link: __url__",
"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.",
@@ -475,7 +476,11 @@
"raisedHand": "__name__ would like to speak.",
"somebody": "Somebody",
"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"
"suboptimalExperienceTitle": "Browser Warning",
"newDeviceCameraTitle": "New camera detected",
"newDeviceMicTitle": "New microphone detected",
"newDeviceCameraTitle": "New audio output detected",
"newDeviceAction": "Use"
},
"passwordSetRemotely": "set by another member",
"poweredby": "powered by",

View File

@@ -25,6 +25,17 @@ function getNewAudioOutputDevice(newDevices) {
d.deviceId === selectedAudioOutputDeviceId)) {
return 'default';
}
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
if (preferredAudioOutputDeviceId
&& preferredAudioOutputDeviceId !== selectedAudioOutputDeviceId
&& availableAudioOutputDevices.find(d => d.deviceId === preferredAudioOutputDeviceId)) {
return preferredAudioOutputDeviceId;
}
}
/**
@@ -39,7 +50,7 @@ function getNewAudioInputDevice(newDevices, localAudio) {
const availableAudioInputDevices = newDevices.filter(
d => d.kind === 'audioinput');
const settings = APP.store.getState()['features/base/settings'];
const selectedAudioInputDeviceId = settings.micDeviceId;
const selectedAudioInputDeviceId = settings.userSelectedMicDeviceId;
const selectedAudioInputDevice = availableAudioInputDevices.find(
d => d.deviceId === selectedAudioInputDeviceId);
@@ -78,7 +89,7 @@ function getNewVideoInputDevice(newDevices, localVideo) {
const availableVideoInputDevices = newDevices.filter(
d => d.kind === 'videoinput');
const settings = APP.store.getState()['features/base/settings'];
const selectedVideoInputDeviceId = settings.cameraDeviceId;
const selectedVideoInputDeviceId = settings.userSelectedCameraDeviceId;
const selectedVideoInputDevice = availableVideoInputDevices.find(
d => d.deviceId === selectedVideoInputDeviceId);

600
package-lock.json generated
View File

@@ -55,23 +55,33 @@
}
},
"@atlaskit/blanket": {
"version": "7.0.12",
"resolved": "https://registry.npmjs.org/@atlaskit/blanket/-/blanket-7.0.12.tgz",
"integrity": "sha512-IWnXU2N42M14kvTU1YhATiK7vGYPZPsk/c2A+b8tNhRJTcfcTxTPMfcmGOvWYPD128el2TSly4uOvn9B9WKc9A==",
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/@atlaskit/blanket/-/blanket-8.0.3.tgz",
"integrity": "sha512-RFV6NsJpgmrmR1nEb5ejFS1V7PlMaK+YN3sYhsnz/61M0NQWIm7y3YGNgwuTvpU5syE5HDrK7fX7aX5IugjXsw==",
"requires": {
"@atlaskit/analytics-next": "^3.1.2",
"@atlaskit/theme": "^7.0.1",
"@atlaskit/analytics-next": "^4.0.3",
"@atlaskit/theme": "^8.1.7",
"@babel/runtime": "^7.0.0"
},
"dependencies": {
"@atlaskit/theme": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/@atlaskit/theme/-/theme-7.0.1.tgz",
"integrity": "sha512-wxXDnkUablJketNCrQuNUuazufYEA7kv0Y6Yzv6uvqfuyNpWUQt4H1psz/MW8DbZmCdku9dEYbNVK3nFP5TDGg==",
"@atlaskit/analytics-next": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@atlaskit/analytics-next/-/analytics-next-4.0.5.tgz",
"integrity": "sha512-IqbKUQugMArrHZcCbm3MM9udbYGOzwE2D4vmiE1E/3waMDP+WybUi/5dwGcGY3FcVkW5/x2Mitt357K4ZOL8Dw==",
"requires": {
"@babel/runtime": "^7.0.0",
"prop-types": "^15.5.10"
}
},
"@atlaskit/theme": {
"version": "8.1.7",
"resolved": "https://registry.npmjs.org/@atlaskit/theme/-/theme-8.1.7.tgz",
"integrity": "sha512-G7LUwdUW9dYMC3Fijpopxh+UeVIUjIB44NJB3MaqzneBmPnSP394Ys1m/pQUaCOMaZYXrwT3oct/jjnp7/nHWQ==",
"requires": {
"@babel/runtime": "^7.0.0",
"exenv": "^1.2.2",
"prop-types": "^15.5.10"
}
}
}
},
@@ -551,6 +561,44 @@
"uuid": "^3.1.0"
}
},
"@atlaskit/form": {
"version": "5.2.10",
"resolved": "https://registry.npmjs.org/@atlaskit/form/-/form-5.2.10.tgz",
"integrity": "sha512-nZa3UQfLbrGwDvoj+zv3S1cPkTgheSj3VVraZl6VHqIhxJXPDBg9B/hFb6Hxrmz21l8q9C29alZ/jTD7BJxXEw==",
"requires": {
"@atlaskit/icon": "^16.0.9",
"@atlaskit/theme": "^8.1.7",
"@babel/runtime": "^7.0.0",
"final-form": "^4.10.0",
"final-form-focus": "^1.1.0",
"memoize-one": "^3.0.1",
"react-uid": "^2.2.0",
"shallow-equal": "^1.0.0",
"tiny-invariant": "^0.0.3"
},
"dependencies": {
"@atlaskit/icon": {
"version": "16.0.9",
"resolved": "https://registry.npmjs.org/@atlaskit/icon/-/icon-16.0.9.tgz",
"integrity": "sha512-1gWuWgBsX6XZwX12iAPjoU0B0na92BZuCG0k8vHAIHJnhrgFpMJP7bqqV1IEob39qgbahk/4G9zagLBaNHWxoQ==",
"requires": {
"@atlaskit/theme": "^8.1.7",
"@babel/runtime": "^7.0.0",
"uuid": "^3.1.0"
}
},
"@atlaskit/theme": {
"version": "8.1.7",
"resolved": "https://registry.npmjs.org/@atlaskit/theme/-/theme-8.1.7.tgz",
"integrity": "sha512-G7LUwdUW9dYMC3Fijpopxh+UeVIUjIB44NJB3MaqzneBmPnSP394Ys1m/pQUaCOMaZYXrwT3oct/jjnp7/nHWQ==",
"requires": {
"@babel/runtime": "^7.0.0",
"exenv": "^1.2.2",
"prop-types": "^15.5.10"
}
}
}
},
"@atlaskit/icon": {
"version": "15.0.3",
"resolved": "https://registry.npmjs.org/@atlaskit/icon/-/icon-15.0.3.tgz",
@@ -713,16 +761,18 @@
}
},
"@atlaskit/modal-dialog": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/@atlaskit/modal-dialog/-/modal-dialog-7.1.2.tgz",
"integrity": "sha512-rY8ojmtr0/9MxqQ8Ab9mtgv1VLRuJMNPCCbstjJzv+NLBu5tIh/on+iuzsxBL84E5hgrvg0wf7JHzKyOoQJQsw==",
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/@atlaskit/modal-dialog/-/modal-dialog-8.0.1.tgz",
"integrity": "sha512-Ips7R8wUhBECbYKbRBC2SiQRUKh1ylJKRajMet9WV47V2WKt7AuRZ8YhNT+RWdp+63/Ze4sm748wfdwv9nfV9Q==",
"requires": {
"@atlaskit/analytics-next": "^3.1.2",
"@atlaskit/blanket": "^7.0.12",
"@atlaskit/button": "^10.1.1",
"@atlaskit/icon": "^15.0.2",
"@atlaskit/portal": "^0.0.17",
"@atlaskit/theme": "^7.0.1",
"@atlaskit/analytics-next": "^4.0.0",
"@atlaskit/blanket": "^8.0.0",
"@atlaskit/button": "^10.1.3",
"@atlaskit/form": "^5.1.8",
"@atlaskit/icon": "^16.0.4",
"@atlaskit/portal": "^0.2.0",
"@atlaskit/textfield": "^0.3.0",
"@atlaskit/theme": "^8.0.0",
"@babel/runtime": "^7.0.0",
"exenv": "^1.2.2",
"prop-types": "^15.5.10",
@@ -733,23 +783,68 @@
"tiny-invariant": "^0.0.3"
},
"dependencies": {
"@atlaskit/icon": {
"version": "15.0.3",
"resolved": "https://registry.npmjs.org/@atlaskit/icon/-/icon-15.0.3.tgz",
"integrity": "sha512-UAf7U0/+5giS2uMlOeVMYmhuWD4fQy0eRcp7r8oEDBqZXNH0yIuHrfu1bPgt2SbFotrjxZdPpOX1i1dXEu7y6g==",
"@atlaskit/analytics-next": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@atlaskit/analytics-next/-/analytics-next-4.0.5.tgz",
"integrity": "sha512-IqbKUQugMArrHZcCbm3MM9udbYGOzwE2D4vmiE1E/3waMDP+WybUi/5dwGcGY3FcVkW5/x2Mitt357K4ZOL8Dw==",
"requires": {
"@atlaskit/theme": "^7.0.1",
"@babel/runtime": "^7.0.0",
"prop-types": "^15.5.10"
}
},
"@atlaskit/button": {
"version": "10.1.3",
"resolved": "https://registry.npmjs.org/@atlaskit/button/-/button-10.1.3.tgz",
"integrity": "sha512-FonEQSaGcjUPUY3JMT0UiPNInGnxot/XPDBxwCTyQP9DLF5GPSWlC1N7KmQV4ljiS/DoSCOSetA27cTBPsw/0Q==",
"requires": {
"@atlaskit/analytics-next": "^4.0.0",
"@atlaskit/analytics-next-types": "^3.1.2",
"@atlaskit/spinner": "10.0.0",
"@atlaskit/theme": "^8.0.0",
"@atlaskit/type-helpers": "^2.0.0",
"@babel/runtime": "^7.0.0",
"babel-runtime": "^6.26.0",
"tslib": "^1.8.0"
}
},
"@atlaskit/icon": {
"version": "16.0.9",
"resolved": "https://registry.npmjs.org/@atlaskit/icon/-/icon-16.0.9.tgz",
"integrity": "sha512-1gWuWgBsX6XZwX12iAPjoU0B0na92BZuCG0k8vHAIHJnhrgFpMJP7bqqV1IEob39qgbahk/4G9zagLBaNHWxoQ==",
"requires": {
"@atlaskit/theme": "^8.1.7",
"@babel/runtime": "^7.0.0",
"uuid": "^3.1.0"
}
},
"@atlaskit/theme": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/@atlaskit/theme/-/theme-7.0.1.tgz",
"integrity": "sha512-wxXDnkUablJketNCrQuNUuazufYEA7kv0Y6Yzv6uvqfuyNpWUQt4H1psz/MW8DbZmCdku9dEYbNVK3nFP5TDGg==",
"@atlaskit/portal": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/@atlaskit/portal/-/portal-0.2.2.tgz",
"integrity": "sha512-RsJG2UAI9e+Nzw7ydhu/pEf0fCwKY69IzV/G2tP/PG2cYgpgc9rvFT0W/lYIoWC8v0Wo9VYYOcTOWWZErLfFKg==",
"requires": {
"@babel/runtime": "^7.0.0",
"exenv": "^1.2.2",
"tiny-invariant": "^0.0.3"
}
},
"@atlaskit/spinner": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/@atlaskit/spinner/-/spinner-10.0.0.tgz",
"integrity": "sha512-zSCde3FyVzLK1LRg6EAEM5unfGO0MkFTjD2EhpHnRvjMCFIhGLa2KFJL6VT/FxvT/RrEDzZwUmIiSK1nXyGIuA==",
"requires": {
"@atlaskit/theme": "^8.0.0",
"@babel/runtime": "^7.0.0",
"babel-runtime": "^6.26.0",
"react-transition-group": "^2.2.1"
}
},
"@atlaskit/theme": {
"version": "8.1.7",
"resolved": "https://registry.npmjs.org/@atlaskit/theme/-/theme-8.1.7.tgz",
"integrity": "sha512-G7LUwdUW9dYMC3Fijpopxh+UeVIUjIB44NJB3MaqzneBmPnSP394Ys1m/pQUaCOMaZYXrwT3oct/jjnp7/nHWQ==",
"requires": {
"@babel/runtime": "^7.0.0",
"exenv": "^1.2.2",
"prop-types": "^15.5.10"
}
}
@@ -927,6 +1022,38 @@
"babel-runtime": "^6.26.0"
}
},
"@atlaskit/textfield": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/@atlaskit/textfield/-/textfield-0.3.1.tgz",
"integrity": "sha512-RRjBFrdAt/UkkJqKPqW+Jc1yle9POsEuHAMuzF1mTGjJfIf6ItP2sF3cTTRXZAOWv7Fo8C+IFTO2R/JpNXQrMg==",
"requires": {
"@atlaskit/analytics-next": "^4.0.1",
"@atlaskit/theme": "^8.0.1",
"@babel/runtime": "^7.0.0",
"emotion": "^9.1.1"
},
"dependencies": {
"@atlaskit/analytics-next": {
"version": "4.0.5",
"resolved": "https://registry.npmjs.org/@atlaskit/analytics-next/-/analytics-next-4.0.5.tgz",
"integrity": "sha512-IqbKUQugMArrHZcCbm3MM9udbYGOzwE2D4vmiE1E/3waMDP+WybUi/5dwGcGY3FcVkW5/x2Mitt357K4ZOL8Dw==",
"requires": {
"@babel/runtime": "^7.0.0",
"prop-types": "^15.5.10"
}
},
"@atlaskit/theme": {
"version": "8.1.7",
"resolved": "https://registry.npmjs.org/@atlaskit/theme/-/theme-8.1.7.tgz",
"integrity": "sha512-G7LUwdUW9dYMC3Fijpopxh+UeVIUjIB44NJB3MaqzneBmPnSP394Ys1m/pQUaCOMaZYXrwT3oct/jjnp7/nHWQ==",
"requires": {
"@babel/runtime": "^7.0.0",
"exenv": "^1.2.2",
"prop-types": "^15.5.10"
}
}
}
},
"@atlaskit/theme": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@atlaskit/theme/-/theme-7.0.2.tgz",
@@ -2058,9 +2185,9 @@
}
},
"@babel/register": {
"version": "7.4.0",
"resolved": "https://registry.npmjs.org/@babel/register/-/register-7.4.0.tgz",
"integrity": "sha512-ekziebXBnS/7V6xk8sBfLSSD6YZuy6P29igBtR6OL/tswKdxOV+Yqq0nzICMguVYtGRZYUCGpfGV8J9Za2iBdw==",
"version": "7.4.4",
"resolved": "https://registry.npmjs.org/@babel/register/-/register-7.4.4.tgz",
"integrity": "sha512-sn51H88GRa00+ZoMqCVgOphmswG4b7mhf9VOB0LUBAieykq2GnRFerlN+JQkO/ntT7wz4jaHNSRPg9IdMPEUkA==",
"requires": {
"core-js": "^3.0.0",
"find-cache-dir": "^2.0.0",
@@ -2240,15 +2367,62 @@
}
}
},
"@expo/react-native-action-sheet": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@expo/react-native-action-sheet/-/react-native-action-sheet-1.1.2.tgz",
"integrity": "sha512-//2EvHVBFVGSAzuJvG0I1UoQVzGJBo2f1CkO+RMnEWdR0FeWYmV7+pCThIroL1czRm/oOtoMxiGS6FgXt6QgVA==",
"@emotion/babel-utils": {
"version": "0.6.10",
"resolved": "https://registry.npmjs.org/@emotion/babel-utils/-/babel-utils-0.6.10.tgz",
"integrity": "sha512-/fnkM/LTEp3jKe++T0KyTszVGWNKPNOUJfjNKLO17BzQ6QPxgbg3whayom1Qr2oLFH3V92tDymU+dT5q676uow==",
"requires": {
"hoist-non-react-statics": "^2.2.2",
"prop-types": "^15.5.10"
"@emotion/hash": "^0.6.6",
"@emotion/memoize": "^0.6.6",
"@emotion/serialize": "^0.9.1",
"convert-source-map": "^1.5.1",
"find-root": "^1.1.0",
"source-map": "^0.7.2"
},
"dependencies": {
"source-map": {
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ=="
}
}
},
"@emotion/hash": {
"version": "0.6.6",
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.6.6.tgz",
"integrity": "sha512-ojhgxzUHZ7am3D2jHkMzPpsBAiB005GF5YU4ea+8DNPybMk01JJUM9V9YRlF/GE95tcOm8DxQvWA2jq19bGalQ=="
},
"@emotion/memoize": {
"version": "0.6.6",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.6.6.tgz",
"integrity": "sha512-h4t4jFjtm1YV7UirAFuSuFGyLa+NNxjdkq6DpFLANNQY5rHueFZHVY+8Cu1HYVP6DrheB0kv4m5xPjo7eKT7yQ=="
},
"@emotion/serialize": {
"version": "0.9.1",
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-0.9.1.tgz",
"integrity": "sha512-zTuAFtyPvCctHBEL8KZ5lJuwBanGSutFEncqLn/m9T1a6a93smBStK+bZzcNPgj4QS8Rkw9VTwJGhRIUVO8zsQ==",
"requires": {
"@emotion/hash": "^0.6.6",
"@emotion/memoize": "^0.6.6",
"@emotion/unitless": "^0.6.7",
"@emotion/utils": "^0.8.2"
}
},
"@emotion/stylis": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.7.1.tgz",
"integrity": "sha512-/SLmSIkN13M//53TtNxgxo57mcJk/UJIDFRKwOiLIBEyBHEcipgR6hNMQ/59Sl4VjCJ0Z/3zeAZyvnSLPG/1HQ=="
},
"@emotion/unitless": {
"version": "0.6.7",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.6.7.tgz",
"integrity": "sha512-Arj1hncvEVqQ2p7Ega08uHLr1JuRYBuO5cIvcA+WWEQ5+VmkOE3ZXzl04NbQxeQpWX78G7u6MqxKuNX3wvYZxg=="
},
"@emotion/utils": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-0.8.2.tgz",
"integrity": "sha512-rLu3wcBWH4P5q1CGoSSH/i9hrXs7SlbRLkoq9IGuoPYNGQvDJ3pt/wmOM+XgYjIDRMVIdkUWt0RsfzF50JfnCw=="
},
"@jitsi/sdp-interop": {
"version": "0.1.14",
"resolved": "https://registry.npmjs.org/@jitsi/sdp-interop/-/sdp-interop-0.1.14.tgz",
@@ -2275,9 +2449,9 @@
}
},
"@react-native-community/cli": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-1.6.0.tgz",
"integrity": "sha512-OV3N5O/wzjb8OTZDiFerX0gf9/KzLJOvDttU38BqIvn8+OLkH6SIgGrKked9vrKKvH5bFG6jmCmKCW5gP+tOwQ==",
"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",
@@ -2360,9 +2534,9 @@
}
},
"node-fetch": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz",
"integrity": "sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA=="
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.5.0.tgz",
"integrity": "sha512-YuZKluhWGJwCcUu4RlZstdAxr8bFfOVHakc1mplwHkk8J+tqM1Y5yraYvIUpeX8aY7+crCwiELJq7Vl0o0LWXw=="
},
"pump": {
"version": "3.0.0",
@@ -2576,8 +2750,7 @@
"abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="
},
"absolute-path": {
"version": "0.0.0",
@@ -2662,7 +2835,7 @@
"blueimp-md5": "^2.10.0",
"json3": "^3.3.2",
"lodash": "^4.17.4",
"ua-parser-js": "github:amplitude/ua-parser-js#ed538f16f5c6ecd8357da989b617d4f156dcf35d"
"ua-parser-js": "github:amplitude/ua-parser-js#ed538f1"
},
"dependencies": {
"ua-parser-js": {
@@ -3325,14 +3498,63 @@
"util.promisify": "^1.0.0"
}
},
"babel-plugin-check-es2015-constants": {
"version": "6.22.0",
"resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz",
"integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=",
"babel-plugin-emotion": {
"version": "9.2.11",
"resolved": "https://registry.npmjs.org/babel-plugin-emotion/-/babel-plugin-emotion-9.2.11.tgz",
"integrity": "sha512-dgCImifnOPPSeXod2znAmgc64NhaaOjGEHROR/M+lmStb3841yK1sgaDYAYMnlvWNz8GnpwIPN0VmNpbWYZ+VQ==",
"requires": {
"babel-runtime": "^6.22.0"
"@babel/helper-module-imports": "^7.0.0",
"@emotion/babel-utils": "^0.6.4",
"@emotion/hash": "^0.6.2",
"@emotion/memoize": "^0.6.1",
"@emotion/stylis": "^0.7.0",
"babel-plugin-macros": "^2.0.0",
"babel-plugin-syntax-jsx": "^6.18.0",
"convert-source-map": "^1.5.0",
"find-root": "^1.1.0",
"mkdirp": "^0.5.1",
"source-map": "^0.5.7",
"touch": "^2.0.1"
}
},
"babel-plugin-macros": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.5.1.tgz",
"integrity": "sha512-xN3KhAxPzsJ6OQTktCanNpIFnnMsCV+t8OloKxIL72D6+SUZYFn9qfklPgef5HyyDtzYZqqb+fs1S12+gQY82Q==",
"requires": {
"@babel/runtime": "^7.4.2",
"cosmiconfig": "^5.2.0",
"resolve": "^1.10.0"
},
"dependencies": {
"@babel/runtime": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.3.tgz",
"integrity": "sha512-9lsJwJLxDh/T3Q3SZszfWOTkk3pHbkmH+3KY+zwIDmsNlxsumuhS2TH3NIpktU4kNvfzy+k3eLT7aTJSPTo0OA==",
"requires": {
"regenerator-runtime": "^0.13.2"
}
},
"regenerator-runtime": {
"version": "0.13.2",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz",
"integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA=="
},
"resolve": {
"version": "1.10.1",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.1.tgz",
"integrity": "sha512-KuIe4mf++td/eFb6wkaPbMDnP6kObCaEtIDuHOUED6MNUo4K670KZUHuuvYPZDxNF0WVLw49n06M2m2dXphEzA==",
"requires": {
"path-parse": "^1.0.6"
}
}
}
},
"babel-plugin-syntax-jsx": {
"version": "6.18.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
"integrity": "sha1-CvMqmm4Tyno/1QaeYtew9Y0NiUY="
},
"babel-plugin-syntax-trailing-function-commas": {
"version": "7.0.0-beta.0",
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-7.0.0-beta.0.tgz",
@@ -4572,6 +4794,20 @@
"elliptic": "^6.0.0"
}
},
"create-emotion": {
"version": "9.2.12",
"resolved": "https://registry.npmjs.org/create-emotion/-/create-emotion-9.2.12.tgz",
"integrity": "sha512-P57uOF9NL2y98Xrbl2OuiDQUZ30GVmASsv5fbsjF4Hlraip2kyAvMm+2PoYUvFFw03Fhgtxk3RqZSm2/qHL9hA==",
"requires": {
"@emotion/hash": "^0.6.2",
"@emotion/memoize": "^0.6.1",
"@emotion/stylis": "^0.7.0",
"@emotion/unitless": "^0.6.2",
"csstype": "^2.5.2",
"stylis": "^3.5.0",
"stylis-rule-sheet": "^0.0.10"
}
},
"create-error": {
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/create-error/-/create-error-0.3.1.tgz",
@@ -4764,6 +5000,11 @@
"source-map": "^0.5.3"
}
},
"csstype": {
"version": "2.6.4",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.4.tgz",
"integrity": "sha512-lAJUJP3M6HxFXbqtGRc0iZrdyeN+WzOWeY0q/VnFzI+kqVrYIzC7bWlKqCW7oCIdzoPkvfp82EVvrTlQ8zsWQg=="
},
"current-executing-script": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/current-executing-script/-/current-executing-script-0.1.3.tgz",
@@ -5169,6 +5410,15 @@
"integrity": "sha1-TapNnbAPmBmIDHn6RXrlsJof04k=",
"dev": true
},
"emotion": {
"version": "9.2.12",
"resolved": "https://registry.npmjs.org/emotion/-/emotion-9.2.12.tgz",
"integrity": "sha512-hcx7jppaI8VoXxIWEhxpDW7I+B4kq9RNzQLmsrF6LY8BGKqe2N+gFAQr0EfuFucFlPs2A9HM4+xNj4NeqEWIOQ==",
"requires": {
"babel-plugin-emotion": "^9.2.11",
"create-emotion": "^9.2.12"
}
},
"encodeurl": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
@@ -6140,6 +6390,34 @@
}
}
},
"final-form": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/final-form/-/final-form-4.12.0.tgz",
"integrity": "sha512-z1fSzDNmIBlDjRMaluM3WgDbcwCFpPm7mvopplgXGMRS49MXR+1n//lteLwPURdGQNOZhWm3GwiEJanEHCItww==",
"requires": {
"@babel/runtime": "^7.3.1"
},
"dependencies": {
"@babel/runtime": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.4.3.tgz",
"integrity": "sha512-9lsJwJLxDh/T3Q3SZszfWOTkk3pHbkmH+3KY+zwIDmsNlxsumuhS2TH3NIpktU4kNvfzy+k3eLT7aTJSPTo0OA==",
"requires": {
"regenerator-runtime": "^0.13.2"
}
},
"regenerator-runtime": {
"version": "0.13.2",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz",
"integrity": "sha512-S/TQAZJO+D3m9xeN1WTI8dLKBBiRgXBlTJvbWjCThHWZj9EvHK70Ff50/tYj2J/fvBY6JtFVwRuazHN2E7M9BA=="
}
}
},
"final-form-focus": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/final-form-focus/-/final-form-focus-1.1.2.tgz",
"integrity": "sha512-Gd+Bd2Ll7ijo3/sd6kJ/bwLkhc2bUJPxTON6fIqee/008EJpACWhT+zoWCm9q6NcfMcWRS+Sp5ikRX8iqdXeGQ=="
},
"finalhandler": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.0.tgz",
@@ -6172,6 +6450,11 @@
"pkg-dir": "^2.0.0"
}
},
"find-root": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng=="
},
"find-up": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
@@ -6252,9 +6535,9 @@
"integrity": "sha512-WQr7DsBZfdmXwqWk7yyk9H2R60iHiUpLMvkov6KivafC9d1SzDTjSBsKMa8skT4laaSxus+F4v7WLO6J0zxPkw=="
},
"focus-lock": {
"version": "0.4.2",
"resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.4.2.tgz",
"integrity": "sha512-jqjj49iZEVT/X8yidEMmP8oWmDKJlJy/um7OyYP42CZ0y4icCp6Fm2lTkZVcFVpt4mZ/hPtbs9ikqId7YhUelw=="
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/focus-lock/-/focus-lock-0.6.3.tgz",
"integrity": "sha512-EU6ePgEauhWrzJEN5RtG1d1ayrWXhEnfzTjnieHj+jG9tNHDEhKTAnCn1TN3gs9h6XWCDH6cpeX1VXY/lzLwZg=="
},
"follow-redirects": {
"version": "1.7.0",
@@ -6433,7 +6716,8 @@
},
"ansi-regex": {
"version": "2.1.1",
"bundled": true
"bundled": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
@@ -6451,11 +6735,13 @@
},
"balanced-match": {
"version": "1.0.0",
"bundled": true
"bundled": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@@ -6468,15 +6754,18 @@
},
"code-point-at": {
"version": "1.1.0",
"bundled": true
"bundled": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true
"bundled": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true
"bundled": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
@@ -6579,7 +6868,8 @@
},
"inherits": {
"version": "2.0.3",
"bundled": true
"bundled": true,
"optional": true
},
"ini": {
"version": "1.3.5",
@@ -6589,6 +6879,7 @@
"is-fullwidth-code-point": {
"version": "1.0.0",
"bundled": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@@ -6601,17 +6892,20 @@
"minimatch": {
"version": "3.0.4",
"bundled": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
},
"minimist": {
"version": "0.0.8",
"bundled": true
"bundled": true,
"optional": true
},
"minipass": {
"version": "2.2.4",
"bundled": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.1",
"yallist": "^3.0.0"
@@ -6628,6 +6922,7 @@
"mkdirp": {
"version": "0.5.1",
"bundled": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@@ -6700,7 +6995,8 @@
},
"number-is-nan": {
"version": "1.0.1",
"bundled": true
"bundled": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
@@ -6710,6 +7006,7 @@
"once": {
"version": "1.4.0",
"bundled": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@@ -6785,7 +7082,8 @@
},
"safe-buffer": {
"version": "5.1.1",
"bundled": true
"bundled": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -6815,6 +7113,7 @@
"string-width": {
"version": "1.0.2",
"bundled": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@@ -6832,6 +7131,7 @@
"strip-ansi": {
"version": "3.0.1",
"bundled": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -6870,11 +7170,13 @@
},
"wrappy": {
"version": "1.0.2",
"bundled": true
"bundled": true,
"optional": true
},
"yallist": {
"version": "3.0.2",
"bundled": true
"bundled": true,
"optional": true
}
}
},
@@ -8569,11 +8871,6 @@
"resolved": "https://registry.npmjs.org/keycode/-/keycode-2.1.9.tgz",
"integrity": "sha1-lkojxU5IiUBbSGGlyfBIDUUUHfo="
},
"keymirror": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/keymirror/-/keymirror-0.1.1.tgz",
"integrity": "sha1-kYiJ6hP40KQufFVyUO7nE63JXDU="
},
"killable": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
@@ -8615,8 +8912,8 @@
}
},
"lib-jitsi-meet": {
"version": "github:jitsi/lib-jitsi-meet#96eef1b1c77590b0ea59217add694f39a73fa896",
"from": "github:jitsi/lib-jitsi-meet#96eef1b1c77590b0ea59217add694f39a73fa896",
"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",
@@ -9280,9 +9577,9 @@
}
},
"node-fetch": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz",
"integrity": "sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA=="
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.5.0.tgz",
"integrity": "sha512-YuZKluhWGJwCcUu4RlZstdAxr8bFfOVHakc1mplwHkk8J+tqM1Y5yraYvIUpeX8aY7+crCwiELJq7Vl0o0LWXw=="
},
"supports-color": {
"version": "5.5.0",
@@ -11580,10 +11877,11 @@
"integrity": "sha1-0uJZ/clR0diQbAiQIAIQjc6HkuU="
},
"react-clientside-effect": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.0.0.tgz",
"integrity": "sha512-XYOjooNlAEos60o4ha9zDb1R9h3LMJUdXJOjFH6fNUwFxXu2k+Wq4Cd0JWmurgeY0DWTzGvpNJ0cY6eHyfhL1Q==",
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/react-clientside-effect/-/react-clientside-effect-1.2.0.tgz",
"integrity": "sha512-cVIsGG7SNHsQsCP4+fw7KFUB0HiYiU8hbvL640XaLCbZ31aK8/lj0qOKJ2K+xRjuQz/IM4Q4qclI0aEqTtcXtA==",
"requires": {
"@babel/runtime": "^7.0.0",
"shallowequal": "^1.1.0"
}
},
@@ -11674,23 +11972,38 @@
}
},
"react-focus-lock": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-1.13.2.tgz",
"integrity": "sha512-ev8OHCuqK3fDpnrlJ8udTdUS6MzpFB3XhXyFp7qLrd9B/OkqSDO/n3t5rVMu/BQEz+NcXFuuSk2hYRgJphsNzA==",
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/react-focus-lock/-/react-focus-lock-1.19.1.tgz",
"integrity": "sha512-TPpfiack1/nF4uttySfpxPk4rGZTLXlaZl7ncZg/ELAk24Iq2B1UUaUioID8H8dneUXqznT83JTNDHDj+kwryw==",
"requires": {
"focus-lock": "^0.4.2",
"@babel/runtime": "^7.0.0",
"focus-lock": "^0.6.3",
"prop-types": "^15.6.2",
"react-clientside-effect": "^1.0.0"
"react-clientside-effect": "^1.2.0"
},
"dependencies": {
"prop-types": {
"version": "15.6.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.6.2.tgz",
"integrity": "sha512-3pboPvLiWD7dkI3qf3KbUe6hKFKa52w+AE0VCqECtf+QHAKgOL37tTaNCnuX1nAAQ4ZhyP+kYVKf8rLmJ/feDQ==",
"loose-envify": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"requires": {
"loose-envify": "^1.3.1",
"object-assign": "^4.1.1"
"js-tokens": "^3.0.0 || ^4.0.0"
}
},
"prop-types": {
"version": "15.7.2",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
"requires": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.8.1"
}
},
"react-is": {
"version": "16.8.6",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
"integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA=="
}
}
},
@@ -11725,9 +12038,9 @@
}
},
"react-native": {
"version": "0.59.4",
"resolved": "https://registry.npmjs.org/react-native/-/react-native-0.59.4.tgz",
"integrity": "sha512-etnXQp9IZgC8Vj5gsxZEDP4xRjJVNIj5/BSE1WcNAONG6tu6+mDBntx1jxHInwh61WYNgoQJuQGsbN5Na59ZDw==",
"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",
@@ -11828,9 +12141,9 @@
}
},
"node-fetch": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.3.0.tgz",
"integrity": "sha512-MOd8pV3fxENbryESLgVIeaGKrdl+uaYhCSSVkjeOb/31/njTpcis5aWfdqgNlHIrKOLRbMnfPINPOML2CIFeXA=="
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.5.0.tgz",
"integrity": "sha512-YuZKluhWGJwCcUu4RlZstdAxr8bFfOVHakc1mplwHkk8J+tqM1Y5yraYvIUpeX8aY7+crCwiELJq7Vl0o0LWXw=="
},
"supports-color": {
"version": "5.5.0",
@@ -11866,37 +12179,11 @@
"jssha": "^2.2.0"
}
},
"react-native-communications": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/react-native-communications/-/react-native-communications-2.2.1.tgz",
"integrity": "sha1-eIO1ayCgAu63kMET+GFuqGksp5U="
},
"react-native-fast-image": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/react-native-fast-image/-/react-native-fast-image-5.1.1.tgz",
"integrity": "sha512-kEzgZxbbXYhy27u5GnhrKitn+XDBFAHSDUJdYC6llMi5cDPjgcqhOAQABj0K+ga5pn+/xPZLmD882rrUGiwVVA=="
},
"react-native-gifted-chat": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/react-native-gifted-chat/-/react-native-gifted-chat-0.6.0.tgz",
"integrity": "sha512-KYI/okKUZmjcJM3I6BP10KG1WNkCKBZhY8N47wk407dr+KqLS4+LR13UKo7j3f++5SrX2Ex+7vYvIQ2pBdzCiA==",
"requires": {
"@expo/react-native-action-sheet": "^1.0.1",
"moment": "^2.19.0",
"react-native-communications": "2.2.1",
"react-native-lightbox": "^0.7.0",
"react-native-parsed-text": "^0.0.20",
"react-native-video": "^3.2.1",
"uuid": "3.3.0"
},
"dependencies": {
"uuid": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.0.tgz",
"integrity": "sha512-ijO9N2xY/YaOqQ5yz5c4sy2ZjWmA6AR6zASb/gdpeKZ8+948CxwfMW9RrKVk5may6ev8c0/Xguu32e2Llelpqw=="
}
}
},
"react-native-google-signin": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/react-native-google-signin/-/react-native-google-signin-1.0.2.tgz",
@@ -11912,28 +12199,11 @@
"resolved": "https://registry.npmjs.org/react-native-keep-awake/-/react-native-keep-awake-4.0.0.tgz",
"integrity": "sha512-0Fotox+eLXQooeibVs3P60yASYUWjtRw9MZNmbuHt5UZQrgUrAKsE4jm7gTr4tPU1m1RkwGzcgUFpcOkh/ec7g=="
},
"react-native-lightbox": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/react-native-lightbox/-/react-native-lightbox-0.7.0.tgz",
"integrity": "sha512-HS3T4WlCd0Gb3us2d6Jse5m6KjNhngnKm35Wapq30WtQa9s+/VMmtuktbGPGaWtswcDyOj6qByeJBw9W80iPCA==",
"requires": {
"prop-types": "^15.5.10"
}
},
"react-native-linear-gradient": {
"version": "2.5.3",
"resolved": "https://registry.npmjs.org/react-native-linear-gradient/-/react-native-linear-gradient-2.5.3.tgz",
"integrity": "sha512-XdusrOXXlkI+yQpUW7YLeiq9cZiBwkvQX4XEkHPVrJ9H47gsKmdgBwObkZBzBQUP0dKK/Sg6aVpETEis4w43bQ=="
},
"react-native-parsed-text": {
"version": "0.0.20",
"resolved": "https://registry.npmjs.org/react-native-parsed-text/-/react-native-parsed-text-0.0.20.tgz",
"integrity": "sha512-n77hYu64Tr3oclzIXBXXaiLh1WbMKdA2Y0x6bX/yqwxAM4afcObENY5VrNB+EsTBJBEDqrypA9D1p2cLEIHkuQ==",
"requires": {
"babel-plugin-check-es2015-constants": "6.22.0",
"prop-types": "^15.5.10"
}
},
"react-native-sound": {
"version": "0.10.12",
"resolved": "https://registry.npmjs.org/react-native-sound/-/react-native-sound-0.10.12.tgz",
@@ -11990,18 +12260,14 @@
}
}
},
"react-native-video": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/react-native-video/-/react-native-video-3.2.1.tgz",
"integrity": "sha512-Xansfoo/to80FwhM1HKlf7pCxDZ5RtV+kG3piCVvsNAhPY4GGwiOGUH9y3Y+mFQIDEWcY8I9j16lsFYAbnue3g==",
"requires": {
"keymirror": "0.1.1",
"prop-types": "^15.5.10"
}
"react-native-watch-connectivity": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/react-native-watch-connectivity/-/react-native-watch-connectivity-0.2.0.tgz",
"integrity": "sha512-l3Quzbb+qa4in2U5RSt/lT0/pHrIpEChT1NnqrVAAXNrjkXjVOsxduaaEDdDhTzNJQEm/PcAcoyrFmgvGOohxw=="
},
"react-native-webrtc": {
"version": "github:jitsi/react-native-webrtc#659d2fe417b52356b1b706636de97e23bae3e9f5",
"from": "github:jitsi/react-native-webrtc#659d2fe417b52356b1b706636de97e23bae3e9f5",
"version": "github:jitsi/react-native-webrtc#4064c6f2db4f8b961daaaa8dafc6a896d7cfbc43",
"from": "github:jitsi/react-native-webrtc#4064c6f2db4f8b961daaaa8dafc6a896d7cfbc43",
"requires": {
"base64-js": "^1.1.2",
"event-target-shim": "^1.0.5",
@@ -12119,6 +12385,11 @@
"tween-functions": "^1.0.1"
}
},
"react-uid": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/react-uid/-/react-uid-2.2.0.tgz",
"integrity": "sha512-z+g5+hFOQ08hCfrGcJ1PNs+cmvH8Uq2CVzCmPeWBsUi5A4W4NWXR5jouledzy3oSKGMU9HOzf8zFuGi15TXJoQ=="
},
"read-pkg": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
@@ -13588,6 +13859,11 @@
"safe-buffer": "^5.0.1"
}
},
"shallow-equal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-1.1.0.tgz",
"integrity": "sha512-0SW1nWo1hnabO62SEeHsl8nmTVVEzguVWZCj5gaQrgWAxz/BaCja4OWdJBWLVPDxdtE/WU7c98uUCCXyPHSCvw=="
},
"shallowequal": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
@@ -14006,9 +14282,9 @@
}
},
"stacktrace-parser": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.5.tgz",
"integrity": "sha512-fjJ563lEMEXdqUH8fZR84sczWxM+Pi3bViix1n7371mFr8sL7UPewec79+IhHkN4UMmGbXoXj58WIaF7lIciRA==",
"version": "0.1.6",
"resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.6.tgz",
"integrity": "sha512-wXhu0Z8YgCGigUtHQq+J7pjXCppk3Um5DwH4qskOKHMlJmKwuuUSm+wDAgU7t4sbVjvuDTNGwOfFKgjMEqSflA==",
"requires": {
"type-fest": "^0.3.0"
}
@@ -14912,6 +15188,24 @@
}
}
},
"touch": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/touch/-/touch-2.0.2.tgz",
"integrity": "sha512-qjNtvsFXTRq7IuMLweVgFxmEuQ6gLbRs2jQxL80TtZ31dEKWYIxRXquij6w6VimyDek5hD3PytljHmEtAs2u0A==",
"requires": {
"nopt": "~1.0.10"
},
"dependencies": {
"nopt": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
"integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=",
"requires": {
"abbrev": "1"
}
}
}
},
"tough-cookie": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
@@ -14983,9 +15277,9 @@
}
},
"type-fest": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.0.tgz",
"integrity": "sha512-fg3sfdDdJDtdHLUpeGsf/fLyG1aapk6zgFiYG5+MDUPybGrJemH4SLk5tP7hGRe8ntxjg0q5LYW53b6YpJIQ9Q=="
"version": "0.3.1",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.3.1.tgz",
"integrity": "sha512-cUGJnCdr4STbePCgqNFbpVNCepa+kAVohJs1sLhxzdH+gnEoOd8VhbYa7pD3zZYGiURWM2xzEII3fQcRizDkYQ=="
},
"type-is": {
"version": "1.6.16",

View File

@@ -26,7 +26,7 @@
"@atlaskit/inline-dialog": "5.3.0",
"@atlaskit/inline-message": "7.0.10",
"@atlaskit/lozenge": "6.2.4",
"@atlaskit/modal-dialog": "7.1.2",
"@atlaskit/modal-dialog": "8.0.1",
"@atlaskit/multi-select": "11.0.13",
"@atlaskit/spinner": "9.0.13",
"@atlaskit/tabs": "8.0.11",
@@ -51,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#96eef1b1c77590b0ea59217add694f39a73fa896",
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#23503b0efd4b97989af7b10696ec9ae790aec7bb",
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
"lodash": "4.17.11",
"moment": "2.19.4",
@@ -62,12 +62,11 @@
"react-emoji-render": "0.4.6",
"react-i18next": "7.13.0",
"react-linkify": "0.2.2",
"react-native": "0.59.4",
"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-gifted-chat": "0.6.0",
"react-native-google-signin": "1.0.2",
"react-native-immersive": "2.0.0",
"react-native-keep-awake": "4.0.0",
@@ -75,7 +74,8 @@
"react-native-sound": "0.10.12",
"react-native-swipeout": "2.3.6",
"react-native-vector-icons": "6.0.2",
"react-native-webrtc": "github:jitsi/react-native-webrtc#659d2fe417b52356b1b706636de97e23bae3e9f5",
"react-native-watch-connectivity": "0.2.0",
"react-native-webrtc": "github:jitsi/react-native-webrtc#4064c6f2db4f8b961daaaa8dafc6a896d7cfbc43",
"react-redux": "5.0.7",
"react-transition-group": "2.4.0",
"redux": "4.0.0",

View File

@@ -1,8 +1,6 @@
import AbstractHandler from './AbstractHandler';
import { amplitude } from './amplitude';
const logger = require('jitsi-meet-logger').getLogger(__filename);
/**
* Analytics handler for Amplitude.
*/
@@ -20,10 +18,7 @@ export default class AmplitudeHandler extends AbstractHandler {
const { amplitudeAPPKey, host } = options;
if (!amplitudeAPPKey) {
logger.warn(
'Failed to initialize Amplitude handler, no APP key');
return;
throw new Error('Failed to initialize Amplitude handler, no APP key');
}
this._enabled = true;

View File

@@ -4,8 +4,6 @@ import { getJitsiMeetGlobalNS } from '../../base/util';
import AbstractHandler from './AbstractHandler';
const logger = require('jitsi-meet-logger').getLogger(__filename);
/**
* Analytics handler for Google Analytics.
*/
@@ -24,11 +22,7 @@ class GoogleAnalyticsHandler extends AbstractHandler {
this._userProperties = {};
if (!options.googleAnalyticsTrackingId) {
logger.warn(
'Failed to initialize Google Analytics handler, no tracking ID'
);
return;
throw new Error('Failed to initialize Google Analytics handler, no tracking ID');
}
this._enabled = true;

View File

@@ -13,6 +13,11 @@ class Amplitude {
* be used only for multi-project logging.
*/
constructor(instanceName) {
// It might not have been included in the build.
if (!AmplitudeNative) {
throw new Error('Amplitude analytics is not supported');
}
this._instanceName = instanceName;
}

View File

@@ -2,6 +2,7 @@
import type { Dispatch } from 'redux';
import { appWillNavigate } from '../base/app';
import { setRoom } from '../base/conference';
import {
configWillLoad,
@@ -58,6 +59,9 @@ export function appNavigate(uri: ?string) {
const { contextRoot, host, room } = location;
const locationURL = new URL(location.toString());
// XXX this looks like CONFIG_WILL_LOAD ?
dispatch(appWillNavigate(locationURL, room));
dispatch(configWillLoad(locationURL, room));
let protocol = location.protocol.toLowerCase();
@@ -81,7 +85,7 @@ export function appNavigate(uri: ?string) {
config = restoreConfig(baseURL);
if (!config) {
dispatch(loadConfigError(error, locationURL));
dispatch(loadConfigError(error, locationURL, room));
return;
}
@@ -92,7 +96,7 @@ export function appNavigate(uri: ?string) {
dispatch(setConfig(config));
dispatch(setRoom(room));
} else {
dispatch(loadConfigError(new Error('Config no longer needed!'), locationURL));
dispatch(loadConfigError(new Error('Config no longer needed!'), locationURL, room));
}
};
}

View File

@@ -22,6 +22,7 @@ import '../../mobile/permissions';
import '../../mobile/picture-in-picture';
import '../../mobile/proximity';
import '../../mobile/wake-lock';
import '../../mobile/watchos';
import { AbstractApp } from './AbstractApp';
import type { Props as AbstractAppProps } from './AbstractApp';

View File

@@ -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));

View File

@@ -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).

View File

@@ -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;
@@ -33,6 +33,25 @@ export function appWillMount(app: Object) {
};
}
/**
* 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
};
}
/**
* Signals that a specific App will unmount (in the terms of React).
*

View File

@@ -18,6 +18,8 @@ import { PersistenceRegistry } from '../../storage';
import { appWillMount, appWillUnmount } from '../actions';
const logger = require('jitsi-meet-logger').getLogger(__filename);
declare var APP: Object;
/**
@@ -74,14 +76,20 @@ export default class BaseApp extends Component<*, State> {
* @type {Promise}
*/
this._init = this._initStorage()
.catch(() => { /* BaseApp should always initialize! */ })
.catch(err => {
/* BaseApp should always initialize! */
logger.error(err);
})
.then(() => new Promise(resolve => {
this.setState({
store: this._createStore()
}, resolve);
}))
.then(() => this.state.store.dispatch(appWillMount(this)))
.catch(() => { /* BaseApp should always initialize! */ });
.catch(err => {
/* BaseApp should always initialize! */
logger.error(err);
});
}
/**

View File

@@ -5,6 +5,9 @@ import {
sendAnalytics
} from '../../analytics';
import { getName } from '../../app';
import { endpointMessageReceived } from '../../subtitles';
import { JITSI_CONNECTION_CONFERENCE_KEY } from '../connection';
import { JitsiConferenceEvents } from '../lib-jitsi-meet';
import { setAudioMuted, setVideoMuted } from '../media';
import {
@@ -15,7 +18,6 @@ import {
participantRoleChanged,
participantUpdated
} from '../participants';
import { endpointMessageReceived } from '../../subtitles';
import { getLocalTracks, trackAdded, trackRemoved } from '../tracks';
import { getJitsiMeetGlobalNS } from '../util';
@@ -378,7 +380,10 @@ export function createConference() {
getWiFiStatsMethod: getJitsiMeetGlobalNS().getWiFiStats
});
connection[JITSI_CONNECTION_CONFERENCE_KEY] = conference;
conference[JITSI_CONFERENCE_URL_KEY] = locationURL;
dispatch(_conferenceWillJoin(conference));
_addConferenceListeners(conference, dispatch);

View File

@@ -38,17 +38,20 @@ export function configWillLoad(locationURL: URL, room: string) {
* loading of a configuration.
* @param {URL} locationURL - The URL of the location which necessitated the
* loading of a configuration.
* @param {string} room - The name of the conference room.
* @returns {{
* type: LOAD_CONFIG_ERROR,
* error: Error,
* locationURL: URL
* locationURL: URL,
* room: string
* }}
*/
export function loadConfigError(error: Error, locationURL: URL) {
export function loadConfigError(error: Error, locationURL: URL, room: ?string) {
return {
type: LOAD_CONFIG_ERROR,
error,
locationURL
locationURL,
room
};
}

View File

@@ -1,4 +1,4 @@
/* @flow */
// @flow
import _ from 'lodash';

View File

@@ -0,0 +1,21 @@
// @flow
import { NativeModules } from 'react-native';
export * from './functions.any';
/**
* Removes all analytics related options from the given configuration, in case of a libre build.
*
* @param {*} config - The configuration which needs to be cleaned up.
* @returns {void}
*/
export function _cleanupConfig(config: Object) {
if (NativeModules.AppInfo.LIBRE_BUILD) {
config.analytics.scriptURLs = [];
delete config.analytics.amplitudeAPPKey;
delete config.analytics.googleAnalyticsTrackingId;
delete config.callStatsID;
delete config.callStatsSecret;
}
}

View File

@@ -0,0 +1,12 @@
// @flow
export * from './functions.any';
/**
* Removes all analytics related options from the given configuration, in case of a libre build.
*
* @param {*} config - The configuration which needs to be cleaned up.
* @returns {void}
*/
export function _cleanupConfig(config: Object) { // eslint-disable-line no-unused-vars
}

View File

@@ -5,6 +5,7 @@ import _ from 'lodash';
import { equals, ReducerRegistry, set } from '../redux';
import { CONFIG_WILL_LOAD, LOAD_CONFIG_ERROR, SET_CONFIG } from './actionTypes';
import { _cleanupConfig } from './functions';
/**
* The initial state of the feature base/config when executing in a
@@ -29,6 +30,8 @@ const INITIAL_NON_RN_STATE = {
* @type {Object}
*/
const INITIAL_RN_STATE = {
analytics: {},
// FIXME The support for audio levels in lib-jitsi-meet polls the statistics
// of WebRTC at a short interval multiple times a second. Unfortunately,
// React Native is slow to fetch these statistics from the native WebRTC
@@ -135,6 +138,8 @@ function _setConfig(state, { config }) {
_getInitialState()
);
_cleanupConfig(newState);
return equals(state, newState) ? state : newState;
}

View File

@@ -3,11 +3,6 @@
import _ from 'lodash';
import type { Dispatch } from 'redux';
import {
conferenceLeft,
conferenceWillLeave,
getCurrentConference
} from '../conference';
import JitsiMeetJS, { JitsiConnectionEvents } from '../lib-jitsi-meet';
import { parseURIString } from '../util';
@@ -18,8 +13,9 @@ import {
CONNECTION_WILL_CONNECT,
SET_LOCATION_URL
} from './actionTypes';
import { JITSI_CONNECTION_URL_KEY } from './constants';
const logger = require('jitsi-meet-logger').getLogger(__filename);
import type JitsiConnection from 'lib-jitsi-meet/JitsiConnection';
/**
* The error structure passed to the {@link connectionFailed} action.
@@ -79,6 +75,7 @@ export function connect(id: ?string, password: ?string) {
return (dispatch: Dispatch<any>, getState: Function) => {
const state = getState();
const options = _constructOptions(state);
const { locationURL } = state['features/base/connection'];
const { issuer, jwt } = state['features/base/jwt'];
const connection
= new JitsiMeetJS.JitsiConnection(
@@ -86,7 +83,9 @@ export function connect(id: ?string, password: ?string) {
jwt && issuer && issuer !== 'anonymous' ? jwt : undefined,
options);
dispatch(_connectionWillConnect(connection));
connection[JITSI_CONNECTION_URL_KEY] = locationURL;
dispatch(connectionWillConnect(connection, locationURL));
connection.addEventListener(
JitsiConnectionEvents.CONNECTION_DISCONNECTED,
@@ -98,7 +97,7 @@ export function connect(id: ?string, password: ?string) {
JitsiConnectionEvents.CONNECTION_FAILED,
_onConnectionFailed);
return connection.connect({
connection.connect({
id,
password
});
@@ -254,16 +253,18 @@ export function connectionFailed(
*
* @param {JitsiConnection} connection - The {@code JitsiConnection} which will
* connect.
* @param {URL} locationURL - FIXME.
* @private
* @returns {{
* type: CONNECTION_WILL_CONNECT,
* connection: JitsiConnection
* }}
*/
function _connectionWillConnect(connection) {
export function connectionWillConnect(connection: JitsiConnection, locationURL: URL) {
return {
type: CONNECTION_WILL_CONNECT,
connection
connection,
locationURL
};
}
@@ -284,14 +285,13 @@ function _constructOptions(state) {
let { bosh } = options;
if (bosh) {
const { locationURL } = state['features/base/connection'];
if (bosh.startsWith('//')) {
// By default our config.js doesn't include the protocol.
const { locationURL } = state['features/base/connection'];
bosh = `${locationURL.protocol}${bosh}`;
} else if (bosh.startsWith('/')) {
// Handle relative URLs, which won't work on mobile.
const { locationURL } = state['features/base/connection'];
const {
protocol,
hostname,
@@ -316,62 +316,6 @@ function _constructOptions(state) {
return options;
}
/**
* Closes connection.
*
* @returns {Function}
*/
export function disconnect() {
return (dispatch: Dispatch<any>, getState: Function): Promise<void> => {
const state = getState();
// The conference we have already joined or are joining.
const conference_ = getCurrentConference(state);
// Promise which completes when the conference has been left and the
// connection has been disconnected.
let promise;
// Leave the conference.
if (conference_) {
// In a fashion similar to JitsiConference's CONFERENCE_LEFT event
// (and the respective Redux action) which is fired after the
// conference has been left, notify the application about the
// intention to leave the conference.
dispatch(conferenceWillLeave(conference_));
promise
= conference_.leave()
.catch(error => {
logger.warn(
'JitsiConference.leave() rejected with:',
error);
// The library lib-jitsi-meet failed to make the
// JitsiConference leave. Which may be because
// JitsiConference thinks it has already left.
// Regardless of the failure reason, continue in
// jitsi-meet as if the leave has succeeded.
dispatch(conferenceLeft(conference_));
});
} else {
promise = Promise.resolve();
}
// Disconnect the connection.
const { connecting, connection } = state['features/base/connection'];
// The connection we have already connected or are connecting.
const connection_ = connection || connecting;
if (connection_) {
promise = promise.then(() => connection_.disconnect());
}
return promise;
};
}
/**
* Sets the location URL of the application, connecton, conference, etc.
*

View File

@@ -12,6 +12,7 @@ import { configureInitialDevices } from '../devices';
export {
connectionEstablished,
connectionFailed,
connectionWillConnect,
setLocationURL
} from './actions.native';

View File

@@ -0,0 +1,19 @@
// @flow
/**
* The name of the {@code JitsiConnection} property which identifies the {@code JitsiConference} currently associated
* with it.
*
* FIXME: This is a hack. It was introduced to solve the following case: if a user presses hangup quickly, they may
* "leave the conference" before the actual conference was ever created. While we might have a connection in place,
* there is no conference which can be left, thus no CONFERENCE_LEFT action will ever be fired.
*
* This is problematic because the external API module used to send events to the native SDK won't know what to send.
* So, in order to detect this situation we are attaching the conference object to the connection which runs it.
*/
export const JITSI_CONNECTION_CONFERENCE_KEY = Symbol('conference');
/**
* The name of the {@code JitsiConnection} property which identifies the location URL where the connection will be made.
*/
export const JITSI_CONNECTION_URL_KEY = Symbol('url');

View File

@@ -3,23 +3,47 @@
import { toState } from '../redux';
/**
* Retrieves a simplified version of the conference/location URL stripped of URL
* params (i.e. Query/search and hash) which should be used for sending invites.
* Retrieves a simplified version of the conference/location URL stripped of URL params (i.e. Query/search and hash)
* which should be used for sending invites.
* NOTE that the method will throw an error if called too early. That is before the conference is joined or before
* the process of joining one has started. This limitation does not apply to the case when called with the URL object
* instance. Use {@link isInviteURLReady} to check if it's safe to call the method already.
*
* @param {Function|Object} stateOrGetState - The redux state or redux's
* {@code getState} function.
* @param {Function|Object} stateOrGetState - The redux state or redux's {@code getState} function or the URL object
* to be stripped.
* @returns {string}
*/
export function getInviteURL(stateOrGetState: Function | Object): string {
const state = toState(stateOrGetState);
const locationURL
let locationURL
= state instanceof URL
? state
: state['features/base/connection'].locationURL;
// If there's no locationURL on the base/connection feature try the base/config where it's set earlier.
if (!locationURL) {
locationURL = state['features/base/config'].locationURL;
}
if (!locationURL) {
throw new Error('Can not get invite URL - the app is not ready');
}
return getURLWithoutParams(locationURL).href;
}
/**
* Checks whether or not is safe to call the {@link getInviteURL} method already.
*
* @param {Function|Object} stateOrGetState - The redux state or redux's {@code getState} function.
* @returns {boolean}
*/
export function isInviteURLReady(stateOrGetState: Function | Object): boolean {
const state = toState(stateOrGetState);
return Boolean(state['features/base/connection'].locationURL || state['features/base/config'].locationURL);
}
/**
* Gets a {@link URL} without hash and query/search params from a specific
* {@code URL}.

View File

@@ -2,6 +2,7 @@
export * from './actions';
export * from './actionTypes';
export * from './constants';
export * from './functions';
import './reducer';

View File

@@ -50,3 +50,15 @@ export const ADD_PENDING_DEVICE_REQUEST = 'ADD_PENDING_DEVICE_REQUEST';
* }
*/
export const REMOVE_PENDING_DEVICE_REQUESTS = 'REMOVE_PENDING_DEVICE_REQUESTS';
/**
* The type of Redux action which will check passed old and passed new devices
* and if needed will show notifications asking the user whether to use those.
*
* {
* type: CHECK_AND_NOTIFY_FOR_NEW_DEVICE
* newDevices: Array<MediaDeviceInfo>
* oldDevices: Array<MediaDeviceInfo>
* }
*/
export const CHECK_AND_NOTIFY_FOR_NEW_DEVICE = 'CHECK_AND_NOTIFY_FOR_NEW_DEVICE';

View File

@@ -3,6 +3,7 @@ import { updateSettings } from '../settings';
import {
ADD_PENDING_DEVICE_REQUEST,
CHECK_AND_NOTIFY_FOR_NEW_DEVICE,
REMOVE_PENDING_DEVICE_REQUESTS,
SET_AUDIO_INPUT_DEVICE,
SET_VIDEO_INPUT_DEVICE,
@@ -90,10 +91,10 @@ export function configureInitialDevices() {
return updateSettingsPromise
.then(() => {
const { audioOutputDeviceId }
const { userSelectedAudioOutputDeviceId }
= getState()['features/base/settings'];
return setAudioOutputDeviceId(audioOutputDeviceId, dispatch)
return setAudioOutputDeviceId(userSelectedAudioOutputDeviceId, dispatch)
.catch(ex => logger.warn(`Failed to set audio output device.
Default audio output device will be used instead ${ex}`));
});
@@ -186,3 +187,21 @@ export function updateDeviceList(devices) {
};
}
/**
* Signals to check new and old devices for newly added devices and notify.
*
* @param {Array<MediaDeviceInfo>} newDevices - Array of the new devices.
* @param {Array<MediaDeviceInfo>} oldDevices - Array of the old devices.
* @returns {{
* type: CHECK_AND_NOTIFY_FOR_NEW_DEVICE,
* newDevices: Array<MediaDeviceInfo>,
* oldDevices: Array<MediaDeviceInfo>
* }}
*/
export function checkAndNotifyForNewDevice(newDevices, oldDevices) {
return {
type: CHECK_AND_NOTIFY_FOR_NEW_DEVICE,
newDevices,
oldDevices
};
}

View File

@@ -117,14 +117,27 @@ export function groupDevicesByKind(devices: Object[]): Object {
*
* @param {string} newId - New audio output device id.
* @param {Function} dispatch - The Redux dispatch function.
* @param {boolean} userSelection - Whether this is a user selection update.
* @returns {Promise}
*/
export function setAudioOutputDeviceId(
newId: string = 'default',
dispatch: Function): Promise<*> {
dispatch: Function,
userSelection: boolean = false): Promise<*> {
return JitsiMeetJS.mediaDevices.setAudioOutputDevice(newId)
.then(() =>
dispatch(updateSettings({
audioOutputDeviceId: newId
})));
.then(() => {
const newSettings = {
audioOutputDeviceId: newId,
userSelectedAudioOutputDeviceId: undefined
};
if (userSelection) {
newSettings.userSelectedAudioOutputDeviceId = newId;
} else {
// a flow workaround, I needed to add 'userSelectedAudioOutputDeviceId: undefined'
delete newSettings.userSelectedAudioOutputDeviceId;
}
return dispatch(updateSettings(newSettings));
});
}

View File

@@ -5,11 +5,21 @@ import { processExternalDeviceRequest } from '../../device-selection';
import { MiddlewareRegistry } from '../redux';
import UIEvents from '../../../../service/UI/UIEvents';
import { removePendingDeviceRequests } from './actions';
import {
removePendingDeviceRequests,
setAudioInputDevice,
setVideoInputDevice
} from './actions';
import {
CHECK_AND_NOTIFY_FOR_NEW_DEVICE,
SET_AUDIO_INPUT_DEVICE,
SET_VIDEO_INPUT_DEVICE
} from './actionTypes';
import { showNotification } from '../../notifications';
import { updateSettings } from '../settings';
import { setAudioOutputDeviceId } from './functions';
const logger = require('jitsi-meet-logger').getLogger(__filename);
/**
* Implements the middleware of the feature base/devices.
@@ -28,6 +38,9 @@ MiddlewareRegistry.register(store => next => action => {
case SET_VIDEO_INPUT_DEVICE:
APP.UI.emitEvent(UIEvents.VIDEO_DEVICE_CHANGED, action.deviceId);
break;
case CHECK_AND_NOTIFY_FOR_NEW_DEVICE:
_checkAndNotifyForNewDevice(store, action.newDevices, action.oldDevices);
break;
}
return next(action);
@@ -63,3 +76,107 @@ function _conferenceJoined({ dispatch, getState }, next, action) {
return result;
}
/**
* Finds a new device by comparing new and old array of devices and dispatches
* notification with the new device.
*
* @param {Store} store - The redux store in which the specified {@code action}
* is being dispatched.
* @param {MediaDeviceInfo[]} newDevices - The array of new devices we received.
* @param {MediaDeviceInfo[]} oldDevices - The array of the old devices we have.
* @private
* @returns {void}
*/
function _checkAndNotifyForNewDevice(store, newDevices, oldDevices) {
const { dispatch } = store;
// let's intersect both newDevices and oldDevices and handle thew newly
// added devices
const onlyNewDevices = newDevices.filter(
nDevice => !oldDevices.find(
device => device.deviceId === nDevice.deviceId));
onlyNewDevices.forEach(newDevice => {
// we want to strip any device details that are not very
// user friendly, like usb ids put in brackets at the end
let description = newDevice.label;
const ix = description.lastIndexOf('(');
if (ix !== -1) {
description = description.substr(0, ix);
}
let titleKey;
switch (newDevice.kind) {
case 'videoinput': {
titleKey = 'notify.newDeviceCameraTitle';
break;
}
case 'audioinput': {
titleKey = 'notify.newDeviceMicTitle';
break;
}
case 'audiooutput': {
titleKey = 'notify.newDeviceCameraTitle';
break;
}
}
dispatch(showNotification({
description,
titleKey,
customActionNameKey: 'notify.newDeviceAction',
customActionHandler: _useDevice.bind(undefined, store, newDevice)
}));
});
}
/**
* Set a device to be currently used, selected by the user.
*
* @param {Store} store - The redux store in which the specified {@code action}
* is being dispatched.
* @param {MediaDeviceInfo} device - The device to save.
* @returns {boolean} - Returns true in order notifications to be dismissed.
* @private
*/
function _useDevice({ dispatch }, device) {
switch (device.kind) {
case 'videoinput': {
dispatch(updateSettings({
userSelectedCameraDeviceId: device.deviceId
}));
dispatch(setVideoInputDevice(device.deviceId));
break;
}
case 'audioinput': {
dispatch(updateSettings({
userSelectedMicDeviceId: device.deviceId
}));
dispatch(setAudioInputDevice(device.deviceId));
break;
}
case 'audiooutput': {
setAudioOutputDeviceId(
device.deviceId,
dispatch,
true)
.then(() => logger.log('changed audio output device'))
.catch(err => {
logger.warn(
'Failed to change audio output device.',
'Default or previously set audio output device will',
' be used instead.',
err);
});
break;
}
}
return true;
}

View File

@@ -1,10 +1,10 @@
// @flow
import React, { Component, type Node } from 'react';
import { TouchableWithoutFeedback, View } from 'react-native';
import React, { PureComponent, type Node } from 'react';
import { SafeAreaView, View } from 'react-native';
import { ColorSchemeRegistry } from '../../../color-scheme';
import { Modal } from '../../../react';
import { SlidingView } from '../../../react';
import { connect } from '../../../redux';
import { StyleType } from '../../../styles';
@@ -33,19 +33,22 @@ type Props = {
};
/**
* A component emulating Android's BottomSheet. For all intents and purposes,
* this component has been designed to work and behave as a {@code Dialog}.
* A component emulating Android's BottomSheet.
*/
class BottomSheet extends Component<Props> {
class BottomSheet extends PureComponent<Props> {
/**
* Initializes a new {@code BottomSheet} instance.
* Assembles a style for the BottomSheet container.
*
* @inheritdoc
* @private
* @returns {StyleType}
*/
constructor(props: Props) {
super(props);
_getContainerStyle() {
const { _styles } = this.props;
this._onCancel = this._onCancel.bind(this);
return {
...styles.container,
backgroundColor: _styles.sheet.backgroundColor
};
}
/**
@@ -57,39 +60,19 @@ class BottomSheet extends Component<Props> {
render() {
const { _styles } = this.props;
return [
<View
key = 'overlay'
style = { styles.overlay } />,
<Modal
key = 'modal'
onRequestClose = { this._onCancel }
visible = { true }>
<View style = { styles.container }>
<TouchableWithoutFeedback
onPress = { this._onCancel } >
<View style = { styles.backdrop } />
</TouchableWithoutFeedback>
return (
<SlidingView
onHide = { this.props.onCancel }
position = 'bottom'
show = { true }>
<SafeAreaView
style = { this._getContainerStyle() }>
<View style = { _styles.sheet }>
{ this.props.children }
</View>
</View>
</Modal>
];
}
_onCancel: () => void;
/**
* Cancels the dialog by calling the onCancel prop callback.
*
* @private
* @returns {void}
*/
_onCancel() {
const { onCancel } = this.props;
onCancel && onCancel();
</SafeAreaView>
</SlidingView>
);
}
}

View File

@@ -28,31 +28,14 @@ export const PLACEHOLDER_COLOR = ColorPalette.lightGrey;
* {@link https://material.io/guidelines/components/bottom-sheets.html}.
*/
export const bottomSheetStyles = createStyleSheet({
/**
* Style for a backdrop which dims the view in the background. This view
* will also be clickable. The backgroundColor is applied to the overlay
* view instead, so the modal animation doesn't affect the backdrop.
*/
backdrop: {
...StyleSheet.absoluteFillObject
},
/**
* Style for the container of the sheet.
*/
container: {
alignItems: 'flex-end',
flex: 1,
flexDirection: 'row',
justifyContent: 'center'
},
/**
* Style for an overlay on top of which the sheet will be displayed.
*/
overlay: {
...StyleSheet.absoluteFillObject,
backgroundColor: 'rgba(127, 127, 127, 0.6)'
bottom: 0,
left: 0,
position: 'absolute',
right: 0
}
});

View File

@@ -17,6 +17,7 @@ import { ADD_KNOWN_DOMAINS } from './actionTypes';
* out of the Andorid manifest.
*/
export const DEFAULT_STATE = [
'alpha.jitsi.net',
'beta.meet.jit.si',
'meet.jit.si',
'8x8.vc'

View File

@@ -2,6 +2,8 @@
import React, { Component } from 'react';
import { getFixedPlatformStyle } from '../../styles';
/**
* {@code AbstractContainer} component's property types.
*/
@@ -80,6 +82,7 @@ export default class AbstractContainer<P: Props> extends Component<P> {
_render(type, props?: P) {
const {
children,
style,
/* eslint-disable no-unused-vars */
@@ -94,7 +97,12 @@ export default class AbstractContainer<P: Props> extends Component<P> {
...filteredProps
} = props || this.props;
const _style = getFixedPlatformStyle(style);
// $FlowFixMe
return React.createElement(type, filteredProps, children);
return React.createElement(type, {
style: _style,
...filteredProps
}, children);
}
}

View File

@@ -110,7 +110,7 @@ export default class SlidingView extends PureComponent<Props, State> {
};
// Bind event handlers so they are only bound once per instance.
this._onHideMenu = this._onHideMenu.bind(this);
this._onHide = this._onHide.bind(this);
}
/**
@@ -158,10 +158,11 @@ export default class SlidingView extends PureComponent<Props, State> {
pointerEvents = 'box-none'
style = { styles.sliderViewContainer } >
<TouchableWithoutFeedback
onPress = { this._onHideMenu } >
onPress = { this._onHide } >
<View style = { styles.sliderViewShadow } />
</TouchableWithoutFeedback>
<Animated.View
pointerEvents = 'box-none'
style = { this._getContentStyle() }>
{ this.props.children }
</Animated.View>
@@ -210,7 +211,7 @@ export default class SlidingView extends PureComponent<Props, State> {
return style;
}
_onHideMenu: () => void;
_onHide: () => void;
/**
* Hides the slider.
@@ -218,15 +219,16 @@ export default class SlidingView extends PureComponent<Props, State> {
* @private
* @returns {void}
*/
_onHideMenu() {
this._setShow(false);
_onHide() {
this._setShow(false)
.then(() => {
const { onHide } = this.props;
const { onHide } = this.props;
onHide && onHide();
onHide && onHide();
});
}
_setShow: (boolean) => void;
_setShow: (boolean) => Promise<*>;
/**
* Shows/hides the slider menu.
@@ -234,32 +236,37 @@ export default class SlidingView extends PureComponent<Props, State> {
* @param {boolean} show - If the slider view is to be made visible,
* {@code true}; otherwise, {@code false}.
* @private
* @returns {void}
* @returns {Promise}
*/
_setShow(show) {
if (!this._mounted) {
return;
}
return new Promise(resolve => {
if (!this._mounted) {
resolve();
const { positionOffset } = this.state;
const { position } = this.props;
let toValue = positionOffset;
return;
}
if (position === 'bottom' || position === 'right') {
toValue = -positionOffset;
}
const { positionOffset } = this.state;
const { position } = this.props;
let toValue = positionOffset;
Animated
.timing(
/* value */ this.state.sliderAnimation,
/* config */ {
duration: 200,
toValue: show ? toValue : 0,
useNativeDriver: true
})
.start(({ finished }) => {
finished && this._mounted && !show
&& this.setState({ showOverlay: false });
});
if (position === 'bottom' || position === 'right') {
toValue = -positionOffset;
}
Animated
.timing(
/* value */ this.state.sliderAnimation,
/* config */ {
duration: 200,
toValue: show ? toValue : 0,
useNativeDriver: true
})
.start(({ finished }) => {
finished && this._mounted && !show
&& this.setState({ showOverlay: false });
resolve();
});
});
}
}

View File

@@ -0,0 +1,86 @@
// @flow
import uuid from 'uuid';
import { toURLString } from '../util';
import type JitsiConference from 'lib-jitsi-meet/JitsiConference';
import type JitsiConnection from 'lib-jitsi-meet/JitsiConnection';
/**
* FIXME.
*/
export class Session {
_conference: ?JitsiConference;
_connection: ?JitsiConnection;
conferenceFailed: boolean;
id: string;
locationURL: URL;
room: string;
/**
* FIXME.
*
* @param {URL} locationURL - FIXME.
* @param {string} room - FIXME.
*/
constructor(locationURL: URL, room: string) {
this.locationURL = locationURL;
this.room = room;
this.id = uuid.v4().toUpperCase();
this.conferenceFailed = false;
}
/**
* FIXME.
*
* @param {JitsiConference} [conference] - FIXME.
*/
set conference(conference: ?JitsiConference) {
if (this._conference && conference && this._conference !== conference) {
throw new Error(`Attempt to reassign conference to ${this.toString()}`);
}
this._conference = conference;
}
/**
* FIXME.
*
* @returns {?JitsiConference}
*/
get conference(): ?JitsiConference {
return this._conference;
}
/**
* FIXME.
*
* @param {JitsiConnection} [connection] - FIXME.
*/
set connection(connection: ?JitsiConnection) {
if (this._connection && connection && this._connection !== connection) {
throw new Error(`Attempt to reassign connection to ${this.toString()}`);
}
this._connection = connection;
}
/**
* FIXME.
*
* @returns {?JitsiConnection}
*/
get connection() {
return this._connection;
}
/**
* FIXME.
*
* @returns {string}
*/
toString() {
return `Session[id=${this.id}, URL: ${toURLString(this.locationURL)} room: ${this.room}]`;
}
}

View File

@@ -0,0 +1,8 @@
export const SESSION_CREATED = 'SESSION_CREATED';
export const SESSION_STARTED = 'SESSION_STARTED';
export const SESSION_TERMINATED = 'SESSION_TERMINATED';
export const SESSION_FAILED = 'SESSION_FAILED';

View File

@@ -0,0 +1,135 @@
// @flow
import { conferenceLeft, conferenceWillLeave } from '../conference';
import { SESSION_CREATED, SESSION_FAILED, SESSION_STARTED, SESSION_TERMINATED } from './actionTypes';
import { Session } from './Session';
import type { Dispatch } from 'redux';
const logger = require('jitsi-meet-logger').getLogger(__filename);
/**
* FIXME.
*
* @returns {Function}
*/
export function endAllSessions() {
return (dispatch: Dispatch<any>, getState: Function) => {
const sessions = getState()['features/base/session'];
for (const session of sessions.values()) {
dispatch(endSession(session));
}
};
}
/**
* FIXME.
*
* @param {Session} session - FIXME.
* @returns {function(*): *}
*/
export function endSession(session: Session) {
return (dispatch: Dispatch<any>) => {
// The conference we have already joined or are joining.
const conference_ = session.conference;
// Promise which completes when the conference has been left and the connection has been disconnected.
let promise;
// Leave the conference.
if (conference_) {
// In a fashion similar to JitsiConference's CONFERENCE_LEFT event (and the respective Redux action) which
// is fired after the conference has been left, notify the application about the intention to leave
// the conference.
dispatch(conferenceWillLeave(conference_));
promise
= conference_.leave()
.catch(error => {
logger.warn('JitsiConference.leave() rejected with:', error);
// The library lib-jitsi-meet failed to make the JitsiConference leave. Which may be because
// JitsiConference thinks it has already left. Regardless of the failure reason, continue in
// jitsi-meet as if the leave has succeeded.
dispatch(conferenceLeft(conference_));
});
} else {
promise = Promise.resolve();
}
const connection_ = session.connection;
if (connection_) {
promise = promise.then(() => connection_.disconnect());
}
return promise;
};
}
/**
* FIXME.
*
* @param {URL} locationURL - FIXME.
* @param {string} room - FIXME.
* @returns {{
* type: SESSION_CREATED,
* session: Session
* }}
*/
export function createSession(locationURL: URL, room: string) {
return {
type: SESSION_CREATED,
session: new Session(locationURL, room)
};
}
/**
* FIXME.
*
* @param {Session} session - FIXME.
* @returns {{
* type: SESSION_FAILED,
* session: Session
* }}
*/
export function sessionFailed(session: Session) {
return {
type: SESSION_FAILED,
session
};
}
/**
* FIXME.
*
* @param {Session} session - FIXME.
* @returns {{
* session: Session,
* type: string
* }}
*/
export function sessionStarted(session: Session) {
return {
type: SESSION_STARTED,
session
};
}
/**
* FIXME.
*
* @param {Session} session - FIXME.
* @returns {{
* type: SESSION_TERMINATED,
* session: Session
* }}
*/
export function sessionTerminated(session: Session) {
return {
type: SESSION_TERMINATED,
session
};
}

View File

@@ -0,0 +1,6 @@
export * from './actions';
export * from './actionTypes';
export * from './selectors';
import './middleware';
import './reducer';

View File

@@ -0,0 +1,164 @@
import { APP_WILL_NAVIGATE } from '../app';
import { CONFERENCE_FAILED, CONFERENCE_JOINED, CONFERENCE_LEFT, CONFERENCE_WILL_JOIN, SET_ROOM } from '../conference';
import { LOAD_CONFIG_ERROR } from '../config';
import { connect, CONNECTION_DISCONNECTED, CONNECTION_FAILED, CONNECTION_WILL_CONNECT } from '../connection';
import { MiddlewareRegistry } from '../redux';
import { createDesiredLocalTracks } from '../tracks';
import { toURLString } from '../util';
import { createSession, endAllSessions, endSession, sessionFailed, sessionStarted, sessionTerminated } from './actions';
import { SESSION_CREATED, SESSION_FAILED, SESSION_STARTED, SESSION_TERMINATED } from './actionTypes';
import { findSessionForConference, findSessionForConnection, findSessionForLocationURL } from './selectors';
MiddlewareRegistry.register(({ dispatch, getState }) => next => action => {
const result = next(action);
switch (action.type) {
case APP_WILL_NAVIGATE: {
const { locationURL, room } = action;
// Currently only one session is allowed at a time
dispatch(endAllSessions());
// Start a new session if there's a conference room name defined
room && room.length && dispatch(createSession(locationURL, room));
break;
}
case CONFERENCE_FAILED: {
const { conference, error } = action;
const { recoverable } = error;
const session = findSessionForConference(getState(), conference);
if (session) {
session.conference = null;
if (typeof recoverable === 'undefined' || recoverable === false) {
session.conferenceFailed = true;
if (session.connection) {
// The sessionFailed is expected to be dispatched from either CONNECTION_DISCONNECTED
// or CONNECTION_FAILED handler in this middleware. The purpose is to delay the SESSION_FAILED until
// the XMPP connection is properly disposed.
dispatch(endSession(session));
} else {
dispatch(sessionFailed(session));
}
}
} else {
console.error('CONFERENCE_FAILED - no session found');
}
break;
}
case CONFERENCE_LEFT: {
const { conference } = action;
const session = findSessionForConference(getState(), conference);
if (session) {
session.conference = null;
// If there's any existing connection wait for it to be closed first
session.connection || dispatch(sessionTerminated(session));
} else {
console.error('CONFERENCE_LEFT - no session found');
}
break;
}
case CONFERENCE_JOINED: {
const { conference } = action;
const session = findSessionForConference(getState(), conference);
if (session) {
dispatch(sessionStarted(session));
} else {
console.error('CONFERENCE_JOINED - no session found');
}
break;
}
case CONFERENCE_WILL_JOIN: {
const { conference } = action;
const { connection } = conference;
const session = findSessionForConnection(getState(), connection);
if (session) {
session.conference = conference;
} else {
console.error('CONFERENCE_WILL_JOIN - no session found');
}
break;
}
case LOAD_CONFIG_ERROR: {
const { locationURL, room } = action;
// There won't be a session if there's no room (it happen when the config is loaded for the welcome page).
if (room) {
const session = findSessionForLocationURL(getState(), locationURL);
if (session) {
dispatch(sessionFailed(session));
} else {
console.error(`LOAD_CONFIG_ERROR - no session found for: ${toURLString(locationURL)}`);
}
}
break;
}
case CONNECTION_DISCONNECTED: {
const { connection } = action;
const session = findSessionForConnection(getState(), connection);
if (session) {
session.connection = null;
dispatch(
session.conferenceFailed ? sessionFailed(session) : sessionTerminated(session));
} else {
console.error('CONNECTION_DISCONNECTED - no session found');
}
break;
}
case CONNECTION_FAILED: {
const { connection, error } = action;
const { recoverable } = error;
const session = findSessionForConnection(getState(), connection);
if (session) {
session.connection = null;
if (typeof recoverable === 'undefined' || recoverable === false) {
dispatch(sessionFailed(session));
}
} else {
console.error('CONNECTION_FAILED - no session found');
}
break;
}
case CONNECTION_WILL_CONNECT: {
const { connection, locationURL } = action;
const session = findSessionForLocationURL(getState(), locationURL);
if (session) {
session.connection = connection;
} else {
console.error(`CONNECTION_WILL_CONNECT - no session found for: ${toURLString(locationURL)}`);
}
break;
}
case SET_ROOM: {
const { locationURL } = getState()['features/base/connection'];
const session = findSessionForLocationURL(getState(), locationURL);
// Web has different logic for creating the local tracks and starting the connection
if (session && typeof APP === 'undefined') {
dispatch(createDesiredLocalTracks());
dispatch(connect());
}
break;
}
case SESSION_CREATED:
case SESSION_STARTED:
case SESSION_FAILED:
case SESSION_TERMINATED:
console.info(`DEBUG ${action.type} ${action.session}`);
break;
}
return result;
});

View File

@@ -0,0 +1,27 @@
import { ReducerRegistry } from '../redux';
import { SESSION_CREATED, SESSION_FAILED, SESSION_TERMINATED } from './actionTypes';
ReducerRegistry.register('features/base/session', (state = new Map(), action) => {
switch (action.type) {
case SESSION_CREATED: {
const { session } = action;
const nextMap = new Map(state);
nextMap.set(session.id, session);
return nextMap;
}
case SESSION_TERMINATED:
case SESSION_FAILED: {
const { session } = action;
const nextMap = new Map(state);
nextMap.delete(session.id);
return nextMap;
}
}
return state;
});

View File

@@ -0,0 +1,56 @@
/**
* FIXME.
*
* @param {Object} state - FIXME.
* @param {JitsiConnection} connection - FIXME.
* @returns {Session|null}
*/
export function findSessionForConnection(state, connection) {
const sessions = state['features/base/session'];
for (const session of sessions.values()) {
if (session.connection === connection) {
return session;
}
}
return null;
}
/**
* FIXME.
*
* @param {Object} state - FIXME.
* @param {JitsiConference} conference - FIXME.
* @returns {Session|null}
*/
export function findSessionForConference(state, conference) {
const sessions = state['features/base/session'];
for (const session of sessions.values()) {
if (session.conference === conference) {
return session;
}
}
return null;
}
/**
* FIXME.
*
* @param {Object} state - FIXME.
* @param {URL} locationURL - FIXME.
* @returns {Session|null}
*/
export function findSessionForLocationURL(state, locationURL) {
const sessions = state['features/base/session'];
for (const session of sessions.values()) {
if (session.locationURL === locationURL) {
return session;
}
}
return null;
}

View File

@@ -30,7 +30,10 @@ const DEFAULT_STATE = {
serverURL: undefined,
startAudioOnly: false,
startWithAudioMuted: false,
startWithVideoMuted: false
startWithVideoMuted: false,
userSelectedAudioOutputDeviceId: undefined,
userSelectedCameraDeviceId: undefined,
userSelectedMicDeviceId: undefined
};
const STORE_NAME = 'features/base/settings';
@@ -38,7 +41,20 @@ const STORE_NAME = 'features/base/settings';
/**
* Sets up the persistence of the feature {@code base/settings}.
*/
PersistenceRegistry.register(STORE_NAME);
const filterSubtree = {};
// start with the default state
Object.keys(DEFAULT_STATE).forEach(key => {
filterSubtree[key] = true;
});
// we want to filter these props, to not be stored as they represent
// what is currently opened/used as devices
filterSubtree.audioOutputDeviceId = false;
filterSubtree.cameraDeviceId = false;
filterSubtree.micDeviceId = false;
PersistenceRegistry.register(STORE_NAME, filterSubtree);
ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
switch (action.type) {

View File

@@ -0,0 +1,18 @@
// @flow
import { type StyleType } from './functions.any';
export * from './functions.any';
/**
* Fixes the style prop that is passed to a platform generic component based on platform specific
* format requirements.
*
* @param {StyleType} style - The passed style prop to the component.
* @returns {StyleType}
*/
export function getFixedPlatformStyle(style: StyleType): StyleType {
// There is nothing to do on mobile - yet.
return style;
}

Some files were not shown because too many files have changed in this diff Show More