mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-01-07 15:22:29 +00:00
Compare commits
89 Commits
new-mobile
...
6360
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5a4ffea9aa | ||
|
|
d8b435ad16 | ||
|
|
b259757c79 | ||
|
|
7995c7ed00 | ||
|
|
08173ac1df | ||
|
|
e33b65da36 | ||
|
|
defd0d2aaf | ||
|
|
c7f96de787 | ||
|
|
29669d88dc | ||
|
|
131eed0f62 | ||
|
|
77da65b8ea | ||
|
|
989b8d9c2b | ||
|
|
eb20e85166 | ||
|
|
d981acb94a | ||
|
|
155a14b351 | ||
|
|
139fa9b3f2 | ||
|
|
f31a7f31e6 | ||
|
|
df887d24a2 | ||
|
|
6286c76904 | ||
|
|
780019a711 | ||
|
|
4d51aedde0 | ||
|
|
058c82a704 | ||
|
|
7e2f3f7d68 | ||
|
|
30b0bb7bd6 | ||
|
|
f02a75bc9d | ||
|
|
6b5a821696 | ||
|
|
ad46df0a1a | ||
|
|
b0deb9ec0c | ||
|
|
61a6ce2a2e | ||
|
|
bacdbeff21 | ||
|
|
7fc9f6f515 | ||
|
|
d34aae4a4b | ||
|
|
ab86d336fb | ||
|
|
ffc412c18d | ||
|
|
077901cd2b | ||
|
|
ba3cd53017 | ||
|
|
0d50f1867d | ||
|
|
e9cfa78aaf | ||
|
|
76f7f3943f | ||
|
|
b9b8090996 | ||
|
|
d42e18c7bb | ||
|
|
a685f096a0 | ||
|
|
49357e3cd2 | ||
|
|
049a3eb7fb | ||
|
|
a31cc62c25 | ||
|
|
75ddf3e75f | ||
|
|
40128277bc | ||
|
|
7770d59c93 | ||
|
|
2dd3c72473 | ||
|
|
4a4856f3de | ||
|
|
47bdf800e7 | ||
|
|
f6d088149c | ||
|
|
cbe3d6d505 | ||
|
|
b41c71e80b | ||
|
|
a4b997362a | ||
|
|
a5da90ddaf | ||
|
|
dffa71666c | ||
|
|
892751154c | ||
|
|
935e4d3261 | ||
|
|
f115028961 | ||
|
|
d910b9db57 | ||
|
|
b2b576f6fb | ||
|
|
a39d9f283d | ||
|
|
0913cf2c4f | ||
|
|
51f7b46628 | ||
|
|
d029045fda | ||
|
|
ddab27e292 | ||
|
|
6df2e4009c | ||
|
|
21cf7f23c2 | ||
|
|
bac1347961 | ||
|
|
c4f39e9c34 | ||
|
|
ee266160f9 | ||
|
|
730d42cba1 | ||
|
|
3f795cd1ff | ||
|
|
252441da29 | ||
|
|
b89c470366 | ||
|
|
6e32a146e3 | ||
|
|
6f02382472 | ||
|
|
de37c3e809 | ||
|
|
ec47f530bc | ||
|
|
7b538fc3e9 | ||
|
|
d5146aaf2e | ||
|
|
efb46df3d9 | ||
|
|
88e6aa3323 | ||
|
|
a7c96e302f | ||
|
|
b85da1e1bb | ||
|
|
e7c5421e33 | ||
|
|
4b4caf5b1c | ||
|
|
8f1fae79e4 |
@@ -26,5 +26,5 @@ android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
android.bundle.enableUncompressedNativeLibs=false
|
||||
|
||||
appVersion=22.3.0
|
||||
sdkVersion=5.2.0
|
||||
appVersion=99.0.0
|
||||
sdkVersion=99.0.0
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
android:name=".JitsiMeetActivity"
|
||||
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
|
||||
android:launchMode="singleTask"
|
||||
android:theme="@style/JitsiMeetActivityStyle"
|
||||
android:resizeableActivity="true"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:windowSoftInputMode="adjustResize"/>
|
||||
|
||||
@@ -1,240 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2018-present 8x8, Inc.
|
||||
* Copyright @ 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.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.FrameLayout;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.facebook.react.ReactRootView;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.rnimmersive.RNImmersiveModule;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
/**
|
||||
* Base class for all views which are backed by a React Native view.
|
||||
*/
|
||||
public abstract class BaseReactView<ListenerT>
|
||||
extends FrameLayout {
|
||||
|
||||
/**
|
||||
* Background color used by {@code BaseReactView} and the React Native root
|
||||
* view.
|
||||
*/
|
||||
protected static int BACKGROUND_COLOR = 0xFF111111;
|
||||
|
||||
/**
|
||||
* The collection of all existing {@code BaseReactView}s. Used to find the
|
||||
* {@code BaseReactView} when delivering events coming from
|
||||
* {@link ExternalAPIModule}.
|
||||
*/
|
||||
static final Set<BaseReactView> views
|
||||
= Collections.newSetFromMap(new WeakHashMap<BaseReactView, Boolean>());
|
||||
|
||||
/**
|
||||
* Finds a {@code BaseReactView} which matches a specific external API
|
||||
* scope.
|
||||
*
|
||||
* @param externalAPIScope - The external API scope associated with the
|
||||
* {@code BaseReactView} to find.
|
||||
* @return The {@code BaseReactView}, if any, associated with the specified
|
||||
* {@code externalAPIScope}; otherwise, {@code null}.
|
||||
*/
|
||||
public static BaseReactView findViewByExternalAPIScope(
|
||||
String externalAPIScope) {
|
||||
synchronized (views) {
|
||||
for (BaseReactView view : views) {
|
||||
if (view.externalAPIScope.equals(externalAPIScope)) {
|
||||
return view;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all registered React views.
|
||||
*
|
||||
* @return An {@link ArrayList} containing all views currently held by React.
|
||||
*/
|
||||
static ArrayList<BaseReactView> getViews() {
|
||||
return new ArrayList<>(views);
|
||||
}
|
||||
|
||||
/**
|
||||
* The unique identifier of this {@code BaseReactView} within the process
|
||||
* for the purposes of {@link ExternalAPIModule}. The name scope was
|
||||
* inspired by postis which we use on Web for the similar purposes of the
|
||||
* iframe-based external API.
|
||||
*/
|
||||
protected String externalAPIScope;
|
||||
|
||||
/**
|
||||
* The listener (e.g. {@link JitsiMeetViewListener}) instance for reporting
|
||||
* events occurring in Jitsi Meet.
|
||||
*/
|
||||
@Deprecated
|
||||
private ListenerT listener;
|
||||
|
||||
/**
|
||||
* React Native root view.
|
||||
*/
|
||||
private ReactRootView reactRootView;
|
||||
|
||||
public BaseReactView(@NonNull Context context) {
|
||||
super(context);
|
||||
initialize((Activity)context);
|
||||
}
|
||||
|
||||
public BaseReactView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
initialize((Activity)context);
|
||||
}
|
||||
|
||||
public BaseReactView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
initialize((Activity)context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the {@code ReactRootView} for the given app name with the given
|
||||
* props. Once created it's set as the view of this {@code FrameLayout}.
|
||||
*
|
||||
* @param appName - The name of the "app" (in React Native terms) to load.
|
||||
* @param props - The React Component props to pass to the app.
|
||||
*/
|
||||
public void createReactRootView(String appName, @Nullable Bundle props) {
|
||||
if (props == null) {
|
||||
props = new Bundle();
|
||||
}
|
||||
|
||||
props.putString("externalAPIScope", externalAPIScope);
|
||||
|
||||
if (reactRootView == null) {
|
||||
reactRootView = new ReactRootView(getContext());
|
||||
reactRootView.startReactApplication(
|
||||
ReactInstanceManagerHolder.getReactInstanceManager(),
|
||||
appName,
|
||||
props);
|
||||
reactRootView.setBackgroundColor(BACKGROUND_COLOR);
|
||||
addView(reactRootView);
|
||||
} else {
|
||||
reactRootView.setAppProperties(props);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases the React resources (specifically the {@link ReactRootView})
|
||||
* associated with this view.
|
||||
*
|
||||
* MUST be called when the {@link Activity} holding this view is destroyed,
|
||||
* typically in the {@code onDestroy} method.
|
||||
*/
|
||||
public void dispose() {
|
||||
if (reactRootView != null) {
|
||||
removeView(reactRootView);
|
||||
reactRootView.unmountReactApplication();
|
||||
reactRootView = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the listener set on this {@code BaseReactView}.
|
||||
*
|
||||
* @return The listener set on this {@code BaseReactView}.
|
||||
*/
|
||||
@Deprecated
|
||||
public ListenerT getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Abstract method called by {@link ExternalAPIModule} when an event is
|
||||
* received for this view.
|
||||
*
|
||||
* @param name - The name of the event.
|
||||
* @param data - The details of the event associated with/specific to the
|
||||
* specified {@code name}.
|
||||
*/
|
||||
@Deprecated
|
||||
protected abstract void onExternalAPIEvent(String name, ReadableMap data);
|
||||
|
||||
@Deprecated
|
||||
protected void onExternalAPIEvent(
|
||||
Map<String, Method> listenerMethods,
|
||||
String name, ReadableMap data) {
|
||||
ListenerT listener = getListener();
|
||||
|
||||
if (listener != null) {
|
||||
ListenerUtils.runListenerMethod(
|
||||
listener, listenerMethods, name, data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the window containing this view gains or loses focus.
|
||||
*
|
||||
* @param hasFocus If the window of this view now has focus, {@code true};
|
||||
* otherwise, {@code false}.
|
||||
*/
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
|
||||
// https://github.com/mockingbot/react-native-immersive#restore-immersive-state
|
||||
RNImmersiveModule immersive = RNImmersiveModule.getInstance();
|
||||
|
||||
if (hasFocus && immersive != null) {
|
||||
immersive.emitImmersiveStateChangeEvent();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a specific listener on this {@code BaseReactView}.
|
||||
*
|
||||
* @param listener The listener to set on this {@code BaseReactView}.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setListener(ListenerT listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
private void initialize(Activity activity) {
|
||||
setBackgroundColor(BACKGROUND_COLOR);
|
||||
|
||||
ReactInstanceManagerHolder.initReactInstanceManager(activity);
|
||||
|
||||
// Hook this BaseReactView into ExternalAPI.
|
||||
externalAPIScope = UUID.randomUUID().toString();
|
||||
synchronized (views) {
|
||||
views.add(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,31 +102,18 @@ class ExternalAPIModule extends ReactContextBaseJavaModule {
|
||||
|
||||
/**
|
||||
* Dispatches an event that occurred on the JavaScript side of the SDK to
|
||||
* the specified {@link BaseReactView}'s listener.
|
||||
* the native side.
|
||||
*
|
||||
* @param name The name of the event.
|
||||
* @param data The details/specifics of the event to send determined
|
||||
* by/associated with the specified {@code name}.
|
||||
* @param scope
|
||||
*/
|
||||
@ReactMethod
|
||||
public void sendEvent(String name, ReadableMap data, String scope) {
|
||||
public void sendEvent(String name, ReadableMap data) {
|
||||
// Keep track of the current ongoing conference.
|
||||
OngoingConferenceTracker.getInstance().onExternalAPIEvent(name, data);
|
||||
|
||||
// The JavaScript App needs to provide uniquely identifying information
|
||||
// to the native ExternalAPI module so that the latter may match the
|
||||
// former to the native BaseReactView which hosts it.
|
||||
BaseReactView view = BaseReactView.findViewByExternalAPIScope(scope);
|
||||
|
||||
if (view != null) {
|
||||
JitsiMeetLogger.d(TAG + " Sending event: " + name + " with data: " + data);
|
||||
try {
|
||||
view.onExternalAPIEvent(name, data);
|
||||
broadcastEmitter.sendBroadcast(name, data);
|
||||
} catch (Exception e) {
|
||||
JitsiMeetLogger.e(e, TAG + " onExternalAPIEvent: error sending event");
|
||||
}
|
||||
}
|
||||
JitsiMeetLogger.d(TAG + " Sending event: " + name + " with data: " + data);
|
||||
broadcastEmitter.sendBroadcast(name, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,12 +167,9 @@ public class JitsiMeetActivity extends AppCompatActivity
|
||||
}
|
||||
}
|
||||
|
||||
public void leave() {
|
||||
if (this.jitsiView != null) {
|
||||
this.jitsiView .leave();
|
||||
} else {
|
||||
JitsiMeetLogger.w("Cannot leave, view is null");
|
||||
}
|
||||
protected void leave() {
|
||||
Intent hangupBroadcastIntent = BroadcastIntentHelper.buildHangUpIntent();
|
||||
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(hangupBroadcastIntent);
|
||||
}
|
||||
|
||||
private @Nullable
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2019-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.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.os.Bundle;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
/**
|
||||
* Base {@link Fragment} for applications integrating Jitsi Meet at a higher level. It
|
||||
* contains all the required wiring between the {@code JitsiMeetView} and
|
||||
* the Fragment lifecycle methods already implemented.
|
||||
*
|
||||
* In this fragment we use a single {@code JitsiMeetView} instance. This
|
||||
* instance gives us access to a view which displays the welcome page and the
|
||||
* conference itself. All lifecycle methods associated with this Fragment are
|
||||
* hooked to the React Native subsystem via proxy calls through the
|
||||
* {@code JitsiMeetActivityDelegate} static methods.
|
||||
*
|
||||
* @deprecated use {@link JitsiMeetActivity} or directly {@link JitsiMeetView}
|
||||
*/
|
||||
@Deprecated
|
||||
public class JitsiMeetFragment extends Fragment {
|
||||
|
||||
/**
|
||||
* Instance of the {@link JitsiMeetView} which this activity will display.
|
||||
*/
|
||||
private JitsiMeetView view;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
return this.view = new JitsiMeetView(getActivity());
|
||||
}
|
||||
|
||||
public JitsiMeetView getJitsiView() {
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
|
||||
JitsiMeetActivityDelegate.onHostDestroy(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
|
||||
JitsiMeetActivityDelegate.onHostResume(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
|
||||
JitsiMeetActivityDelegate.onHostPause(getActivity());
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
|
||||
private boolean isAudioMuted;
|
||||
|
||||
static void launch(Context context, HashMap<String, Object> extraData) {
|
||||
public static void launch(Context context, HashMap<String, Object> extraData) {
|
||||
OngoingNotification.createOngoingConferenceNotificationChannel();
|
||||
|
||||
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||
@@ -80,7 +80,7 @@ public class JitsiMeetOngoingConferenceService extends Service
|
||||
}
|
||||
}
|
||||
|
||||
static void abort(Context context) {
|
||||
public static void abort(Context context) {
|
||||
Intent intent = new Intent(context, JitsiMeetOngoingConferenceService.class);
|
||||
context.stopService(intent);
|
||||
}
|
||||
|
||||
@@ -16,37 +16,33 @@
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.oney.WebRTCModule.WebRTCModule;
|
||||
import com.facebook.react.ReactRootView;
|
||||
import com.rnimmersive.RNImmersiveModule;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Map;
|
||||
|
||||
public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener>
|
||||
implements OngoingConferenceTracker.OngoingConferenceListener {
|
||||
public class JitsiMeetView extends FrameLayout {
|
||||
|
||||
/**
|
||||
* The {@code Method}s of {@code JitsiMeetViewListener} by event name i.e.
|
||||
* redux action types.
|
||||
* Background color used by {@code BaseReactView} and the React Native root
|
||||
* view.
|
||||
*/
|
||||
private static final Map<String, Method> LISTENER_METHODS
|
||||
= ListenerUtils.mapListenerMethods(JitsiMeetViewListener.class);
|
||||
private static final int BACKGROUND_COLOR = 0xFF111111;
|
||||
|
||||
/**
|
||||
* The URL of the current conference.
|
||||
* React Native root view.
|
||||
*/
|
||||
// XXX Currently, one thread writes and one thread reads, so it should be
|
||||
// fine to have this field volatile without additional synchronization.
|
||||
private volatile String url;
|
||||
private ReactRootView reactRootView;
|
||||
|
||||
/**
|
||||
* Helper method to recursively merge 2 {@link Bundle} objects representing React Native props.
|
||||
@@ -110,10 +106,19 @@ public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener>
|
||||
initialize(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
/**
|
||||
* Releases the React resources (specifically the {@link ReactRootView})
|
||||
* associated with this view.
|
||||
*
|
||||
* MUST be called when the {@link Activity} holding this view is destroyed,
|
||||
* typically in the {@code onDestroy} method.
|
||||
*/
|
||||
public void dispose() {
|
||||
OngoingConferenceTracker.getInstance().removeListener(this);
|
||||
super.dispose();
|
||||
if (reactRootView != null) {
|
||||
removeView(reactRootView);
|
||||
reactRootView.unmountReactApplication();
|
||||
reactRootView = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,14 +131,17 @@ public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener>
|
||||
* page.
|
||||
*/
|
||||
public void enterPictureInPicture() {
|
||||
try {
|
||||
WebRTCModule pipModule
|
||||
= ReactInstanceManagerHolder.getNativeModule(
|
||||
WebRTCModule.class);
|
||||
pipModule.addDecryptors();
|
||||
}
|
||||
catch (Exception e) {
|
||||
int a = 1;
|
||||
PictureInPictureModule pipModule
|
||||
= ReactInstanceManagerHolder.getNativeModule(
|
||||
PictureInPictureModule.class);
|
||||
if (pipModule != null
|
||||
&& pipModule.isPictureInPictureSupported()
|
||||
&& !JitsiMeetActivityDelegate.arePermissionsBeingRequested()) {
|
||||
try {
|
||||
pipModule.enterPictureInPicture();
|
||||
} catch (RuntimeException re) {
|
||||
JitsiMeetLogger.e(re, "Failed to enter PiP mode");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -148,10 +156,40 @@ public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener>
|
||||
}
|
||||
|
||||
/**
|
||||
* Leaves the currently active conference.
|
||||
* Creates the {@code ReactRootView} for the given app name with the given
|
||||
* props. Once created it's set as the view of this {@code FrameLayout}.
|
||||
*
|
||||
* @param appName - The name of the "app" (in React Native terms) to load.
|
||||
* @param props - The React Component props to pass to the app.
|
||||
*/
|
||||
public void leave() {
|
||||
setProps(new Bundle());
|
||||
private void createReactRootView(String appName, @Nullable Bundle props) {
|
||||
if (props == null) {
|
||||
props = new Bundle();
|
||||
}
|
||||
|
||||
if (reactRootView == null) {
|
||||
reactRootView = new ReactRootView(getContext());
|
||||
reactRootView.startReactApplication(
|
||||
ReactInstanceManagerHolder.getReactInstanceManager(),
|
||||
appName,
|
||||
props);
|
||||
reactRootView.setBackgroundColor(BACKGROUND_COLOR);
|
||||
addView(reactRootView);
|
||||
} else {
|
||||
reactRootView.setAppProperties(props);
|
||||
}
|
||||
}
|
||||
|
||||
private void initialize(@NonNull Context context) {
|
||||
// Check if the parent Activity implements JitsiMeetActivityInterface,
|
||||
// otherwise things may go wrong.
|
||||
if (!(context instanceof JitsiMeetActivityInterface)) {
|
||||
throw new RuntimeException("Enclosing Activity must implement JitsiMeetActivityInterface");
|
||||
}
|
||||
|
||||
setBackgroundColor(BACKGROUND_COLOR);
|
||||
|
||||
ReactInstanceManagerHolder.initReactInstanceManager((Activity) context);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -176,46 +214,27 @@ public class JitsiMeetView extends BaseReactView<JitsiMeetViewListener>
|
||||
createReactRootView("App", props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for {@link OngoingConferenceTracker} events.
|
||||
* @param conferenceUrl
|
||||
*/
|
||||
@Override
|
||||
public void onCurrentConferenceChanged(String conferenceUrl) {
|
||||
// This property was introduced in order to address
|
||||
// an exception in the Picture-in-Picture functionality which arose
|
||||
// because of delays related to bridging between JavaScript and Java. To
|
||||
// reduce these delays do not wait for the call to be transferred to the
|
||||
// UI thread.
|
||||
this.url = conferenceUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for {@link ExternalAPIModule} events.
|
||||
*
|
||||
* @param name The name of the event.
|
||||
* @param data The details/specifics of the event to send determined
|
||||
* by/associated with the specified {@code name}.
|
||||
*/
|
||||
@Override
|
||||
@Deprecated
|
||||
protected void onExternalAPIEvent(String name, ReadableMap data) {
|
||||
onExternalAPIEvent(LISTENER_METHODS, name, data);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
dispose();
|
||||
super.onDetachedFromWindow();
|
||||
}
|
||||
|
||||
private void initialize(@NonNull Context context) {
|
||||
// Check if the parent Activity implements JitsiMeetActivityInterface,
|
||||
// otherwise things may go wrong.
|
||||
if (!(context instanceof JitsiMeetActivityInterface)) {
|
||||
throw new RuntimeException("Enclosing Activity must implement JitsiMeetActivityInterface");
|
||||
}
|
||||
/**
|
||||
* Called when the window containing this view gains or loses focus.
|
||||
*
|
||||
* @param hasFocus If the window of this view now has focus, {@code true};
|
||||
* otherwise, {@code false}.
|
||||
*/
|
||||
@Override
|
||||
public void onWindowFocusChanged(boolean hasFocus) {
|
||||
super.onWindowFocusChanged(hasFocus);
|
||||
|
||||
OngoingConferenceTracker.getInstance().addListener(this);
|
||||
// https://github.com/mockingbot/react-native-immersive#restore-immersive-state
|
||||
RNImmersiveModule immersive = RNImmersiveModule.getInstance();
|
||||
|
||||
if (hasFocus && immersive != null) {
|
||||
immersive.emitImmersiveStateChangeEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2017-present 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.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Interface for listening to events coming from Jitsi Meet.
|
||||
*/
|
||||
@Deprecated
|
||||
public interface JitsiMeetViewListener {
|
||||
/**
|
||||
* Called when a conference was joined.
|
||||
*
|
||||
* @param data Map with a "url" key with the conference URL.
|
||||
*/
|
||||
void onConferenceJoined(Map<String, Object> data);
|
||||
|
||||
/**
|
||||
* Called when the active conference ends, be it because of user choice or
|
||||
* because of a failure.
|
||||
*
|
||||
* @param data Map with an "error" key with the error and a "url" key with
|
||||
* the conference URL. If the conference finished gracefully no `error`
|
||||
* key will be present. The possible values for "error" are described here:
|
||||
* https://github.com/jitsi/lib-jitsi-meet/blob/master/JitsiConnectionErrors.js
|
||||
* https://github.com/jitsi/lib-jitsi-meet/blob/master/JitsiConferenceErrors.js
|
||||
*/
|
||||
void onConferenceTerminated(Map<String, Object> data);
|
||||
|
||||
/**
|
||||
* Called before the conference is joined.
|
||||
*
|
||||
* @param data Map with a "url" key with the conference URL.
|
||||
*/
|
||||
void onConferenceWillJoin(Map<String, Object> data);
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
/*
|
||||
* Copyright @ 2018-present 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.
|
||||
*/
|
||||
|
||||
package org.jitsi.meet.sdk;
|
||||
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.ReadableMapKeySetIterator;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Utility methods for helping with transforming {@link ExternalAPIModule}
|
||||
* events into listener methods. Used with descendants of {@link BaseReactView}.
|
||||
*/
|
||||
@Deprecated
|
||||
public final class ListenerUtils {
|
||||
/**
|
||||
* Extracts the methods defined in a listener and creates a mapping of this
|
||||
* form: event name -> method.
|
||||
*
|
||||
* @param listener - The listener whose methods we want to slurp.
|
||||
* @return A mapping with event names - methods.
|
||||
*/
|
||||
public static Map<String, Method> mapListenerMethods(Class listener) {
|
||||
Map<String, Method> methods = new HashMap<>();
|
||||
|
||||
// Figure out the mapping between the listener methods
|
||||
// and the events i.e. redux action types.
|
||||
Pattern onPattern = Pattern.compile("^on[A-Z]+");
|
||||
Pattern camelcasePattern = Pattern.compile("([a-z0-9]+)([A-Z0-9]+)");
|
||||
|
||||
for (Method method : listener.getDeclaredMethods()) {
|
||||
// * The method must be public (because it is declared by an
|
||||
// interface).
|
||||
// * The method must be/return void.
|
||||
if (!Modifier.isPublic(method.getModifiers())
|
||||
|| !Void.TYPE.equals(method.getReturnType())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// * The method name must start with "on" followed by a
|
||||
// capital/uppercase letter (in agreement with the camelcase
|
||||
// coding style customary to Java in general and the projects of
|
||||
// the Jitsi community in particular).
|
||||
String name = method.getName();
|
||||
|
||||
if (!onPattern.matcher(name).find()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// * The method must accept/have exactly 1 parameter of a type
|
||||
// assignable from HashMap.
|
||||
Class<?>[] parameterTypes = method.getParameterTypes();
|
||||
|
||||
if (parameterTypes.length != 1
|
||||
|| !parameterTypes[0].isAssignableFrom(HashMap.class)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Convert the method name to an event name.
|
||||
name
|
||||
= camelcasePattern.matcher(name.substring(2))
|
||||
.replaceAll("$1_$2")
|
||||
.toUpperCase(Locale.ROOT);
|
||||
methods.put(name, method);
|
||||
}
|
||||
|
||||
return methods;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the right listener method for the given event.
|
||||
* NOTE: This function will run asynchronously on the UI thread.
|
||||
*
|
||||
* @param listener - The listener on which the method will be called.
|
||||
* @param listenerMethods - Mapping with event names and the matching
|
||||
* methods.
|
||||
* @param eventName - Name of the event.
|
||||
* @param eventData - Data associated with the event.
|
||||
*/
|
||||
public static void runListenerMethod(
|
||||
final Object listener,
|
||||
final Map<String, Method> listenerMethods,
|
||||
final String eventName,
|
||||
final ReadableMap eventData) {
|
||||
// Make sure listener methods are invoked on the UI thread. It
|
||||
// was requested by SDK consumers.
|
||||
if (UiThreadUtil.isOnUiThread()) {
|
||||
runListenerMethodOnUiThread(
|
||||
listener, listenerMethods, eventName, eventData);
|
||||
} else {
|
||||
UiThreadUtil.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
runListenerMethodOnUiThread(
|
||||
listener, listenerMethods, eventName, eventData);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper companion for {@link ListenerUtils#runListenerMethod} which runs
|
||||
* in the UI thread.
|
||||
*/
|
||||
private static void runListenerMethodOnUiThread(
|
||||
Object listener,
|
||||
Map<String, Method> listenerMethods,
|
||||
String eventName,
|
||||
ReadableMap eventData) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
|
||||
Method method = listenerMethods.get(eventName);
|
||||
if (method != null) {
|
||||
try {
|
||||
method.invoke(listener, toHashMap(eventData));
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
} catch (InvocationTargetException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes a new {@code HashMap} instance with the key-value
|
||||
* associations of a specific {@code ReadableMap}.
|
||||
*
|
||||
* @param readableMap the {@code ReadableMap} specifying the key-value
|
||||
* associations with which the new {@code HashMap} instance is to be
|
||||
* initialized.
|
||||
* @return a new {@code HashMap} instance initialized with the key-value
|
||||
* associations of the specified {@code readableMap}.
|
||||
*/
|
||||
private static HashMap<String, Object> toHashMap(ReadableMap readableMap) {
|
||||
HashMap<String, Object> hashMap = new HashMap<>();
|
||||
|
||||
for (ReadableMapKeySetIterator i = readableMap.keySetIterator();
|
||||
i.hasNextKey();) {
|
||||
String key = i.nextKey();
|
||||
|
||||
hashMap.put(key, readableMap.getString(key));
|
||||
}
|
||||
|
||||
return hashMap;
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,6 @@ import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.module.annotations.ReactModule;
|
||||
import com.oney.WebRTCModule.WebRTCModule;
|
||||
|
||||
import org.jitsi.meet.sdk.log.JitsiMeetLogger;
|
||||
|
||||
@@ -85,10 +84,34 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
public void enterPictureInPicture() {
|
||||
WebRTCModule pipModule
|
||||
= ReactInstanceManagerHolder.getNativeModule(
|
||||
WebRTCModule.class);
|
||||
pipModule.addDecryptors();
|
||||
if (!isEnabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isSupported) {
|
||||
throw new IllegalStateException("Picture-in-Picture not supported");
|
||||
}
|
||||
|
||||
Activity currentActivity = getCurrentActivity();
|
||||
|
||||
if (currentActivity == null) {
|
||||
throw new IllegalStateException("No current Activity!");
|
||||
}
|
||||
|
||||
JitsiMeetLogger.i(TAG + " Entering Picture-in-Picture");
|
||||
|
||||
PictureInPictureParams.Builder builder
|
||||
= new PictureInPictureParams.Builder()
|
||||
.setAspectRatio(new Rational(1, 1));
|
||||
|
||||
// https://developer.android.com/reference/android/app/Activity.html#enterPictureInPictureMode(android.app.PictureInPictureParams)
|
||||
//
|
||||
// The system may disallow entering picture-in-picture in various cases,
|
||||
// including when the activity is not visible, if the screen is locked
|
||||
// or if the user has an activity pinned.
|
||||
if (!currentActivity.enterPictureInPictureMode(builder.build())) {
|
||||
throw new RuntimeException("Failed to enter Picture-in-Picture");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -100,10 +123,12 @@ class PictureInPictureModule extends ReactContextBaseJavaModule {
|
||||
*/
|
||||
@ReactMethod
|
||||
public void enterPictureInPicture(Promise promise) {
|
||||
WebRTCModule pipModule
|
||||
= ReactInstanceManagerHolder.getNativeModule(
|
||||
WebRTCModule.class);
|
||||
pipModule.addDecryptors();
|
||||
try {
|
||||
enterPictureInPicture();
|
||||
promise.resolve(null);
|
||||
} catch (RuntimeException re) {
|
||||
promise.reject(re);
|
||||
}
|
||||
}
|
||||
|
||||
@ReactMethod
|
||||
|
||||
@@ -35,8 +35,6 @@ import com.oney.WebRTCModule.RTCVideoViewManager;
|
||||
import com.oney.WebRTCModule.WebRTCModule;
|
||||
|
||||
import org.devio.rn.splashscreen.SplashScreenModule;
|
||||
import org.webrtc.Loggable;
|
||||
import org.webrtc.Logging;
|
||||
import org.webrtc.SoftwareVideoDecoderFactory;
|
||||
import org.webrtc.SoftwareVideoEncoderFactory;
|
||||
import org.webrtc.audio.AudioDeviceModule;
|
||||
@@ -59,13 +57,6 @@ class ReactInstanceManagerHolder {
|
||||
*/
|
||||
private static ReactInstanceManager reactInstanceManager;
|
||||
|
||||
private static Loggable webrtcLogger = new Loggable() {
|
||||
@Override
|
||||
public void onLogMessage(String message, Logging.Severity severity, String tag) {
|
||||
Log.d(tag,message);
|
||||
}
|
||||
};
|
||||
|
||||
private static List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
|
||||
List<NativeModule> nativeModules
|
||||
= new ArrayList<>(Arrays.<NativeModule>asList(
|
||||
@@ -97,8 +88,6 @@ class ReactInstanceManagerHolder {
|
||||
|
||||
options.setVideoDecoderFactory(new SoftwareVideoDecoderFactory());
|
||||
options.setVideoEncoderFactory(new SoftwareVideoEncoderFactory());
|
||||
options.setInjectableLogger(webrtcLogger);
|
||||
options.setLoggingSeverity(Logging.Severity.LS_VERBOSE);
|
||||
|
||||
nativeModules.add(new WebRTCModule(reactContext, options));
|
||||
|
||||
|
||||
3
android/sdk/src/main/res/values/styles.xml
Normal file
3
android/sdk/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
<style name="JitsiMeetActivityStyle" parent="Theme.AppCompat.Light.NoActionBar"/>
|
||||
</resources>
|
||||
@@ -3070,7 +3070,7 @@ export default {
|
||||
/**
|
||||
* Leaves the room.
|
||||
*
|
||||
* @param {boolean} doDisconnect - Wether leaving the room should also terminate the connection.
|
||||
* @param {boolean} doDisconnect - Whether leaving the room should also terminate the connection.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
async leaveRoom(doDisconnect = true) {
|
||||
@@ -3094,34 +3094,12 @@ export default {
|
||||
* @param email {string} the new email
|
||||
*/
|
||||
changeLocalEmail(email = '') {
|
||||
const localParticipant = getLocalParticipant(APP.store.getState());
|
||||
|
||||
const formattedEmail = String(email).trim();
|
||||
|
||||
if (formattedEmail === localParticipant.email) {
|
||||
return;
|
||||
}
|
||||
|
||||
const localId = localParticipant.id;
|
||||
|
||||
APP.store.dispatch(participantUpdated({
|
||||
// XXX Only the local participant is allowed to update without
|
||||
// stating the JitsiConference instance (i.e. participant property
|
||||
// `conference` for a remote participant) because the local
|
||||
// participant is uniquely identified by the very fact that there is
|
||||
// only one local participant.
|
||||
|
||||
id: localId,
|
||||
local: true,
|
||||
email: formattedEmail
|
||||
}));
|
||||
|
||||
APP.store.dispatch(updateSettings({
|
||||
email: formattedEmail
|
||||
}));
|
||||
APP.API.notifyEmailChanged(localId, {
|
||||
email: formattedEmail
|
||||
});
|
||||
|
||||
sendData(commands.EMAIL, formattedEmail);
|
||||
},
|
||||
|
||||
@@ -3130,29 +3108,12 @@ export default {
|
||||
* @param url {string} the new url
|
||||
*/
|
||||
changeLocalAvatarUrl(url = '') {
|
||||
const { avatarURL, id } = getLocalParticipant(APP.store.getState());
|
||||
|
||||
const formattedUrl = String(url).trim();
|
||||
|
||||
if (formattedUrl === avatarURL) {
|
||||
return;
|
||||
}
|
||||
|
||||
APP.store.dispatch(participantUpdated({
|
||||
// XXX Only the local participant is allowed to update without
|
||||
// stating the JitsiConference instance (i.e. participant property
|
||||
// `conference` for a remote participant) because the local
|
||||
// participant is uniquely identified by the very fact that there is
|
||||
// only one local participant.
|
||||
|
||||
id,
|
||||
local: true,
|
||||
avatarURL: formattedUrl
|
||||
}));
|
||||
|
||||
APP.store.dispatch(updateSettings({
|
||||
avatarURL: formattedUrl
|
||||
}));
|
||||
|
||||
sendData(commands.AVATAR_URL, url);
|
||||
},
|
||||
|
||||
@@ -3193,23 +3154,6 @@ export default {
|
||||
*/
|
||||
changeLocalDisplayName(nickname = '') {
|
||||
const formattedNickname = getNormalizedDisplayName(nickname);
|
||||
const { id, name } = getLocalParticipant(APP.store.getState());
|
||||
|
||||
if (formattedNickname === name) {
|
||||
return;
|
||||
}
|
||||
|
||||
APP.store.dispatch(participantUpdated({
|
||||
// XXX Only the local participant is allowed to update without
|
||||
// stating the JitsiConference instance (i.e. participant property
|
||||
// `conference` for a remote participant) because the local
|
||||
// participant is uniquely identified by the very fact that there is
|
||||
// only one local participant.
|
||||
|
||||
id,
|
||||
local: true,
|
||||
name: formattedNickname
|
||||
}));
|
||||
|
||||
APP.store.dispatch(updateSettings({
|
||||
displayName: formattedNickname
|
||||
|
||||
103
config.js
103
config.js
@@ -93,6 +93,10 @@ var config = {
|
||||
// Enables sending multiple video streams, i.e., camera and desktop tracks can be shared in the conference
|
||||
// separately as two different streams instead of one composite stream.
|
||||
// sendMultipleVideoStreams: false
|
||||
|
||||
// Signal that this client supports receiving multiple video streams. Without this flag jicofo will enable
|
||||
// multi-stream backward compatibility.
|
||||
receiveMultipleVideoStreams: true
|
||||
},
|
||||
|
||||
// Disables moderator indicators.
|
||||
@@ -143,6 +147,7 @@ var config = {
|
||||
|
||||
// Disable measuring of audio levels.
|
||||
// disableAudioLevels: false,
|
||||
|
||||
// audioLevelsInterval: 200,
|
||||
|
||||
// Enabling this will run the lib-jitsi-meet no audio detection module which
|
||||
@@ -271,8 +276,9 @@ var config = {
|
||||
|
||||
// Recording
|
||||
|
||||
// Whether to enable file recording or not.
|
||||
// DEPRECATED. Use recordingService.enabled instead.
|
||||
// fileRecordingsEnabled: false,
|
||||
|
||||
// Enable the dropbox integration.
|
||||
// dropbox: {
|
||||
// appKey: '<APP_KEY>' // Specify your app key here.
|
||||
@@ -282,14 +288,27 @@ var config = {
|
||||
// redirectURI:
|
||||
// 'https://jitsi-meet.example.com/subfolder/static/oauth.html'
|
||||
// },
|
||||
// When integrations like dropbox are enabled only that will be shown,
|
||||
// by enabling fileRecordingsServiceEnabled, we show both the integrations
|
||||
// and the generic recording service (its configuration and storage type
|
||||
// depends on jibri configuration)
|
||||
|
||||
// recordingService: {
|
||||
// // When integrations like dropbox are enabled only that will be shown,
|
||||
// // by enabling fileRecordingsServiceEnabled, we show both the integrations
|
||||
// // and the generic recording service (its configuration and storage type
|
||||
// // depends on jibri configuration)
|
||||
// enabled: false,
|
||||
|
||||
// // Whether to show the possibility to share file recording with other people
|
||||
// // (e.g. meeting participants), based on the actual implementation
|
||||
// // on the backend.
|
||||
// sharingEnabled: false,
|
||||
|
||||
// // Hide the warning that says we only store the recording for 24 hours.
|
||||
// hideStorageWarning: false
|
||||
// },
|
||||
|
||||
// DEPRECATED. Use recordingService.enabled instead.
|
||||
// fileRecordingsServiceEnabled: false,
|
||||
// Whether to show the possibility to share file recording with other people
|
||||
// (e.g. meeting participants), based on the actual implementation
|
||||
// on the backend.
|
||||
|
||||
// DEPRECATED. Use recordingService.sharingEnabled instead.
|
||||
// fileRecordingsServiceSharingEnabled: false,
|
||||
|
||||
// Whether to enable live streaming or not.
|
||||
@@ -299,29 +318,51 @@ var config = {
|
||||
// localRecording: {
|
||||
// // Whether to disable local recording or not.
|
||||
// disable: false,
|
||||
|
||||
// // Whether to notify all participants when a participant is recording locally.
|
||||
// notifyAllParticipants: false
|
||||
// notifyAllParticipants: false,
|
||||
|
||||
// // Whether to disable the self recording feature (only local participant streams).
|
||||
// disableSelfRecording: false
|
||||
// },
|
||||
|
||||
// Transcription (in interface_config,
|
||||
// subtitles and buttons can be configured)
|
||||
// DEPRECATED. Use transcription.enabled instead.
|
||||
// transcribingEnabled: false,
|
||||
|
||||
// If true transcriber will use the application language.
|
||||
// The application language is either explicitly set by participants in their settings or automatically
|
||||
// detected based on the environment, e.g. if the app is opened in a chrome instance which is using french as its
|
||||
// default language then transcriptions for that participant will be in french.
|
||||
// Defaults to true.
|
||||
// DEPRECATED. Use transcription.useAppLanguage instead.
|
||||
// transcribeWithAppLanguage: true,
|
||||
|
||||
// Transcriber language. This settings will only work if "transcribeWithAppLanguage" is explicitly set to false.
|
||||
// Available languages can be found in
|
||||
// ./src/react/features/transcribing/transcriber-langs.json.
|
||||
// DEPRECATED. Use transcription.preferredLanguage instead.
|
||||
// preferredTranscribeLanguage: 'en-US',
|
||||
|
||||
// Enables automatic turning on captions when recording is started
|
||||
// DEPRECATED. Use transcription.autoCaptionOnRecord instead.
|
||||
// autoCaptionOnRecord: false,
|
||||
|
||||
// Transcription options.
|
||||
// transcription: {
|
||||
// // Whether the feature should be enabled or not.
|
||||
// enabled: false,
|
||||
|
||||
// // If true transcriber will use the application language.
|
||||
// // The application language is either explicitly set by participants in their settings or automatically
|
||||
// // detected based on the environment, e.g. if the app is opened in a chrome instance which
|
||||
// // is using french as its default language then transcriptions for that participant will be in french.
|
||||
// // Defaults to true.
|
||||
// useAppLanguage: true,
|
||||
|
||||
// // Transcriber language. This settings will only work if "useAppLanguage"
|
||||
// // is explicitly set to false.
|
||||
// // Available languages can be found in
|
||||
// // ./src/react/features/transcribing/transcriber-langs.json.
|
||||
// preferredLanguage: 'en-US',
|
||||
|
||||
// // Disable start transcription for all participants.
|
||||
// disableStartForAll: false,
|
||||
|
||||
// // Enables automatic turning on captions when recording is started
|
||||
// autoCaptionOnRecord: false
|
||||
// },
|
||||
|
||||
// Misc
|
||||
|
||||
// Default value for the channel "last N" attribute. -1 for unlimited.
|
||||
@@ -383,7 +424,7 @@ var config = {
|
||||
// // This will result in Safari not being able to decode video from endpoints sending VP9 video.
|
||||
// // When set to false, the conference falls back to VP8 whenever there is an endpoint that doesn't support the
|
||||
// // preferred codec and goes back to the preferred codec when that endpoint leaves.
|
||||
// // enforcePreferredCodec: false,
|
||||
// enforcePreferredCodec: false,
|
||||
//
|
||||
// // Provides a way to configure the maximum bitrates that will be enforced on the simulcast streams for
|
||||
// // video tracks. The keys in the object represent the type of the stream (LD, SD or HD) and the values
|
||||
@@ -551,8 +592,8 @@ var config = {
|
||||
// enableFeaturesBasedOnToken: false,
|
||||
|
||||
// When enabled the password used for locking a room is restricted to up to the number of digits specified
|
||||
// roomPasswordNumberOfDigits: 10,
|
||||
// default: roomPasswordNumberOfDigits: false,
|
||||
// roomPasswordNumberOfDigits: 10,
|
||||
|
||||
// Message to show the users. Example: 'The service will be down for
|
||||
// maintenance at 01:00 AM GMT,
|
||||
@@ -576,7 +617,7 @@ var config = {
|
||||
// },
|
||||
|
||||
// When 'true', the user cannot edit the display name.
|
||||
// (Mainly useful when used in conjuction with the JWT so the JWT name becomes read only.)
|
||||
// (Mainly useful when used in conjunction with the JWT so the JWT name becomes read only.)
|
||||
// readOnlyName: false,
|
||||
|
||||
// If etherpad integration is enabled, setting this to true will
|
||||
@@ -991,7 +1032,7 @@ var config = {
|
||||
// maxConferenceSize: 200,
|
||||
//
|
||||
// // The maximum number of e2e ping messages per second for the whole conference to aim for.
|
||||
// // This is used to contol the pacing of messages in order to reduce the load on the backend.
|
||||
// // This is used to control the pacing of messages in order to reduce the load on the backend.
|
||||
// maxMessagesPerSecond: 250
|
||||
// },
|
||||
|
||||
@@ -1101,7 +1142,7 @@ var config = {
|
||||
field02Hover: 'red',
|
||||
action01: 'green',
|
||||
action01Hover: 'lightgreen',
|
||||
action02Disabled: 'beige',
|
||||
disabled01: 'beige',
|
||||
success02: 'cadetblue',
|
||||
action02Hover: 'aliceblue'
|
||||
},
|
||||
@@ -1173,7 +1214,8 @@ var config = {
|
||||
// 'transcribing',
|
||||
// 'video-quality',
|
||||
// 'insecure-room',
|
||||
// 'highlight-moment'
|
||||
// 'highlight-moment',
|
||||
// 'top-panel-toggle'
|
||||
// ]
|
||||
// },
|
||||
|
||||
@@ -1367,7 +1409,14 @@ var config = {
|
||||
|
||||
// // Disables the stage filmstrip
|
||||
// // (displaying multiple participants on stage besides the vertical filmstrip)
|
||||
// disableStageFilmstrip: false
|
||||
// disableStageFilmstrip: false,
|
||||
|
||||
// // Disables the top panel (only shown when a user is sharing their screen).
|
||||
// disableTopPanel: false,
|
||||
|
||||
// // The minimum number of participants that must be in the call for
|
||||
// // the top panel layout to be used.
|
||||
// minParticipantCountForTopPanel: 50
|
||||
// },
|
||||
|
||||
// Tile view related config options.
|
||||
|
||||
@@ -21,8 +21,7 @@ import { isFatalJitsiConnectionError } from './react/features/base/lib-jitsi-mee
|
||||
import { getCustomerDetails } from './react/features/jaas/actions.any';
|
||||
import { isVpaasMeeting, getJaasJWT } from './react/features/jaas/functions';
|
||||
import {
|
||||
setPrejoinDisplayNameRequired,
|
||||
setPrejoinPageVisibility
|
||||
setPrejoinDisplayNameRequired
|
||||
} from './react/features/prejoin/actions';
|
||||
const logger = Logger.getLogger(__filename);
|
||||
|
||||
@@ -247,7 +246,6 @@ function requestAuth(roomName) {
|
||||
resolve(connection);
|
||||
};
|
||||
|
||||
APP.store.dispatch(setPrejoinPageVisibility(false));
|
||||
APP.store.dispatch(
|
||||
openDialog(LoginDialog, { onSuccess,
|
||||
roomName })
|
||||
|
||||
@@ -88,7 +88,7 @@ ol.poll-result-list {
|
||||
margin-bottom: 4;
|
||||
}
|
||||
|
||||
// Needeed to override atlaskit default blue color
|
||||
// Needed to override atlaskit default blue color
|
||||
.poll-create-container .jsYMHu {
|
||||
background: #292929;
|
||||
border-color: #808090;
|
||||
|
||||
@@ -19,6 +19,10 @@
|
||||
font-size: 14px;
|
||||
margin-left: 16px;
|
||||
}
|
||||
|
||||
&.space-top {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.recording-header-line {
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
&#autoHide.with-always-on {
|
||||
overflow: hidden;
|
||||
animation: hideSubject forwards .6s ease-out;
|
||||
margin-left: 4px;
|
||||
|
||||
& > .subject-info-container {
|
||||
justify-content: flex-start;
|
||||
|
||||
@@ -112,7 +112,7 @@
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove unnecssary padding that is normally used to prevent horizontal
|
||||
* Remove unnecessary padding that is normally used to prevent horizontal
|
||||
* filmstrip from overlapping the left edge of the screen.
|
||||
*/
|
||||
#filmstripLocalVideo,
|
||||
|
||||
16
debian/jitsi-meet-tokens.postinst
vendored
16
debian/jitsi-meet-tokens.postinst
vendored
@@ -50,27 +50,15 @@ case "$1" in
|
||||
if [ -f "$PROSODY_HOST_CONFIG" ] ; then
|
||||
# search for the token auth, if this is not enabled this is the
|
||||
# first time we install tokens package and needs a config change
|
||||
if ! egrep -q '^\s*authentication\s*=\s*"token"' "$PROSODY_HOST_CONFIG"; then
|
||||
if ! egrep -q '^\s*authentication\s*=\s*"token" -- do not delete me' "$PROSODY_HOST_CONFIG"; then
|
||||
# enable tokens in prosody host config
|
||||
sed -i 's/--plugin_paths/plugin_paths/g' $PROSODY_HOST_CONFIG
|
||||
sed -i 's/authentication = "anonymous"/authentication = "token"/g' $PROSODY_HOST_CONFIG
|
||||
sed -i 's/ --allow_unencrypted_plain_auth/ allow_unencrypted_plain_auth/g' $PROSODY_HOST_CONFIG
|
||||
sed -i 's/authentication = "jitsi-anonymous" -- do not delete me/authentication = "token" -- do not delete me/g' $PROSODY_HOST_CONFIG
|
||||
sed -i "s/ --app_id=\"example_app_id\"/ app_id=\"$APP_ID\"/g" $PROSODY_HOST_CONFIG
|
||||
sed -i "s/ --app_secret=\"example_app_secret\"/ app_secret=\"$APP_SECRET\"/g" $PROSODY_HOST_CONFIG
|
||||
sed -i 's/ --modules_enabled = { "token_verification" }/ modules_enabled = { "token_verification" }/g' $PROSODY_HOST_CONFIG
|
||||
sed -i '/^\s*--\s*"token_verification"/ s/--\s*//' $PROSODY_HOST_CONFIG
|
||||
|
||||
PR10_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'prosody-0.10' 2>/dev/null | awk '{print $3}' || true)"
|
||||
PRTRUNK_INSTALL_CHECK="$(dpkg-query -f '${Status}' -W 'prosody-trunk' 2>/dev/null | awk '{print $3}' || true)"
|
||||
PR_VER_INSTALLED=$(dpkg-query -f='${Version}\n' --show prosody 2>/dev/null || true)
|
||||
if [ "$PR10_INSTALL_CHECK" = "installed" ] \
|
||||
|| [ "$PR10_INSTALL_CHECK" = "unpacked" ] \
|
||||
|| [ "$PRTRUNK_INSTALL_CHECK" = "installed" ] \
|
||||
|| [ "$PRTRUNK_INSTALL_CHECK" = "unpacked" ] \
|
||||
|| dpkg --compare-versions "$PR_VER_INSTALLED" lt "0.11" ; then
|
||||
sed -i 's/module:hook_global(/module:hook(/g' /usr/share/jitsi-meet/prosody-plugins/mod_auth_token.lua
|
||||
fi
|
||||
|
||||
if [ -x "/etc/init.d/prosody" ]; then
|
||||
invoke-rc.d prosody restart || true
|
||||
fi
|
||||
|
||||
2
debian/jitsi-meet-tokens.postrm
vendored
2
debian/jitsi-meet-tokens.postrm
vendored
@@ -37,7 +37,7 @@ case "$1" in
|
||||
APP_SECRET=$RET
|
||||
|
||||
# Revert prosody config
|
||||
sed -i 's/authentication = "token"/authentication = "anonymous"/g' $PROSODY_HOST_CONFIG
|
||||
sed -i 's/authentication = "token" -- do not delete me/authentication = "jitsi-anonymous" -- do not delete me/g' $PROSODY_HOST_CONFIG
|
||||
sed -i "s/ app_id=\"$APP_ID\"/ --app_id=\"example_app_id\"/g" $PROSODY_HOST_CONFIG
|
||||
sed -i "s/ app_secret=\"$APP_SECRET\"/ --app_secret=\"example_app_secret\"/g" $PROSODY_HOST_CONFIG
|
||||
sed -i '/^\s*"token_verification"/ s/"token_verification"/-- "token_verification"/' $PROSODY_HOST_CONFIG
|
||||
|
||||
@@ -36,8 +36,7 @@ unlimited_jids = {
|
||||
}
|
||||
|
||||
VirtualHost "jitmeet.example.com"
|
||||
-- enabled = false -- Remove this line to enable this host
|
||||
authentication = "anonymous"
|
||||
authentication = "jitsi-anonymous" -- do not delete me
|
||||
-- Properties below are modified by jitsi-meet-tokens package config
|
||||
-- and authentication above is switched to "token"
|
||||
--app_id="example_app_id"
|
||||
@@ -146,6 +145,7 @@ VirtualHost "jigasi.meet.jitsi"
|
||||
modules_enabled = {
|
||||
"ping";
|
||||
"bosh";
|
||||
"muc_password_check";
|
||||
}
|
||||
authentication = "token"
|
||||
app_id = "jitsi";
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
server_names_hash_bucket_size 64;
|
||||
|
||||
types {
|
||||
# nginx's default mime.types doesn't include a mapping for wasm
|
||||
# nginx's default mime.types doesn't include a mapping for wasm or wav.
|
||||
application/wasm wasm;
|
||||
audio/wav wav;
|
||||
}
|
||||
upstream prosody {
|
||||
zone upstreams 64K;
|
||||
@@ -73,6 +74,13 @@ server {
|
||||
alias /usr/share/jitsi-meet/libs/external_api.min.js;
|
||||
}
|
||||
|
||||
location = /_api/room-info {
|
||||
proxy_pass http://prosody/room-info?prefix=$prefix&$args;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header X-Forwarded-For $remote_addr;
|
||||
proxy_set_header Host $http_host;
|
||||
}
|
||||
|
||||
# ensure all static content can always be found first
|
||||
location ~ ^/(libs|css|static|images|fonts|lang|sounds|connection_optimization|.well-known)/(.*)$
|
||||
{
|
||||
@@ -156,6 +164,14 @@ server {
|
||||
rewrite ^/(.*)$ /xmpp-websocket;
|
||||
}
|
||||
|
||||
location ~ ^/([^/?&:'"]+)/_api/room-info {
|
||||
set $subdomain "$1.";
|
||||
set $subdir "$1/";
|
||||
set $prefix "$1";
|
||||
|
||||
rewrite ^/(.*)$ /_api/room-info;
|
||||
}
|
||||
|
||||
# Anything that didn't match above, and isn't a real file, assume it's a room name and redirect to /
|
||||
location ~ ^/([^/?&:'"]+)/(.*)$ {
|
||||
set $subdomain "$1.";
|
||||
|
||||
@@ -144,7 +144,7 @@ var interfaceConfig = {
|
||||
RECENT_LIST_ENABLED: true,
|
||||
REMOTE_THUMBNAIL_RATIO: 1, // 1:1
|
||||
|
||||
SETTINGS_SECTIONS: [ 'devices', 'language', 'moderator', 'profile', 'calendar', 'sounds' ],
|
||||
SETTINGS_SECTIONS: [ 'devices', 'language', 'moderator', 'profile', 'calendar', 'sounds', 'more' ],
|
||||
|
||||
/**
|
||||
* Specify which sharing features should be displayed. If the value is not set
|
||||
|
||||
@@ -9,9 +9,9 @@ install! 'cocoapods', :deterministic_uuids => false
|
||||
target 'JitsiMeet' do
|
||||
project 'app/app.xcodeproj'
|
||||
|
||||
pod 'Firebase/Analytics', '~> 6.33.0'
|
||||
pod 'Firebase/Crashlytics', '~> 6.33.0'
|
||||
pod 'Firebase/DynamicLinks', '~> 6.33.0'
|
||||
pod 'Firebase/Analytics', '~> 8.0'
|
||||
pod 'Firebase/Crashlytics', '~> 8.0'
|
||||
pod 'Firebase/DynamicLinks', '~> 8.0'
|
||||
end
|
||||
|
||||
target 'JitsiMeetSDK' do
|
||||
|
||||
188
ios/Podfile.lock
188
ios/Podfile.lock
@@ -21,50 +21,60 @@ PODS:
|
||||
- React-Core (= 0.68.1)
|
||||
- React-jsi (= 0.68.1)
|
||||
- ReactCommon/turbomodule/core (= 0.68.1)
|
||||
- Firebase/Analytics (6.33.0):
|
||||
- Firebase/Analytics (8.15.0):
|
||||
- Firebase/Core
|
||||
- Firebase/Core (6.33.0):
|
||||
- Firebase/Core (8.15.0):
|
||||
- Firebase/CoreOnly
|
||||
- FirebaseAnalytics (= 6.8.3)
|
||||
- Firebase/CoreOnly (6.33.0):
|
||||
- FirebaseCore (= 6.10.3)
|
||||
- Firebase/Crashlytics (6.33.0):
|
||||
- FirebaseAnalytics (~> 8.15.0)
|
||||
- Firebase/CoreOnly (8.15.0):
|
||||
- FirebaseCore (= 8.15.0)
|
||||
- Firebase/Crashlytics (8.15.0):
|
||||
- Firebase/CoreOnly
|
||||
- FirebaseCrashlytics (~> 4.6.1)
|
||||
- Firebase/DynamicLinks (6.33.0):
|
||||
- FirebaseCrashlytics (~> 8.15.0)
|
||||
- Firebase/DynamicLinks (8.15.0):
|
||||
- Firebase/CoreOnly
|
||||
- FirebaseDynamicLinks (~> 4.3.1)
|
||||
- FirebaseAnalytics (6.8.3):
|
||||
- FirebaseCore (~> 6.10)
|
||||
- FirebaseInstallations (~> 1.6)
|
||||
- GoogleAppMeasurement (= 6.8.3)
|
||||
- GoogleUtilities/AppDelegateSwizzler (~> 6.7)
|
||||
- GoogleUtilities/MethodSwizzler (~> 6.7)
|
||||
- GoogleUtilities/Network (~> 6.7)
|
||||
- "GoogleUtilities/NSData+zlib (~> 6.7)"
|
||||
- nanopb (~> 1.30906.0)
|
||||
- FirebaseCore (6.10.3):
|
||||
- FirebaseCoreDiagnostics (~> 1.6)
|
||||
- GoogleUtilities/Environment (~> 6.7)
|
||||
- GoogleUtilities/Logger (~> 6.7)
|
||||
- FirebaseCoreDiagnostics (1.7.0):
|
||||
- GoogleDataTransport (~> 7.4)
|
||||
- GoogleUtilities/Environment (~> 6.7)
|
||||
- GoogleUtilities/Logger (~> 6.7)
|
||||
- nanopb (~> 1.30906.0)
|
||||
- FirebaseCrashlytics (4.6.2):
|
||||
- FirebaseCore (~> 6.10)
|
||||
- FirebaseInstallations (~> 1.6)
|
||||
- GoogleDataTransport (~> 7.2)
|
||||
- nanopb (~> 1.30906.0)
|
||||
- PromisesObjC (~> 1.2)
|
||||
- FirebaseDynamicLinks (4.3.1):
|
||||
- FirebaseCore (~> 6.10)
|
||||
- FirebaseInstallations (1.7.0):
|
||||
- FirebaseCore (~> 6.10)
|
||||
- GoogleUtilities/Environment (~> 6.7)
|
||||
- GoogleUtilities/UserDefaults (~> 6.7)
|
||||
- PromisesObjC (~> 1.2)
|
||||
- FirebaseDynamicLinks (~> 8.15.0)
|
||||
- FirebaseAnalytics (8.15.0):
|
||||
- FirebaseAnalytics/AdIdSupport (= 8.15.0)
|
||||
- FirebaseCore (~> 8.0)
|
||||
- FirebaseInstallations (~> 8.0)
|
||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
|
||||
- GoogleUtilities/MethodSwizzler (~> 7.7)
|
||||
- GoogleUtilities/Network (~> 7.7)
|
||||
- "GoogleUtilities/NSData+zlib (~> 7.7)"
|
||||
- nanopb (~> 2.30908.0)
|
||||
- FirebaseAnalytics/AdIdSupport (8.15.0):
|
||||
- FirebaseCore (~> 8.0)
|
||||
- FirebaseInstallations (~> 8.0)
|
||||
- GoogleAppMeasurement (= 8.15.0)
|
||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
|
||||
- GoogleUtilities/MethodSwizzler (~> 7.7)
|
||||
- GoogleUtilities/Network (~> 7.7)
|
||||
- "GoogleUtilities/NSData+zlib (~> 7.7)"
|
||||
- nanopb (~> 2.30908.0)
|
||||
- FirebaseCore (8.15.0):
|
||||
- FirebaseCoreDiagnostics (~> 8.0)
|
||||
- GoogleUtilities/Environment (~> 7.7)
|
||||
- GoogleUtilities/Logger (~> 7.7)
|
||||
- FirebaseCoreDiagnostics (8.15.0):
|
||||
- GoogleDataTransport (~> 9.1)
|
||||
- GoogleUtilities/Environment (~> 7.7)
|
||||
- GoogleUtilities/Logger (~> 7.7)
|
||||
- nanopb (~> 2.30908.0)
|
||||
- FirebaseCrashlytics (8.15.0):
|
||||
- FirebaseCore (~> 8.0)
|
||||
- FirebaseInstallations (~> 8.0)
|
||||
- GoogleDataTransport (~> 9.1)
|
||||
- GoogleUtilities/Environment (~> 7.7)
|
||||
- nanopb (~> 2.30908.0)
|
||||
- PromisesObjC (< 3.0, >= 1.2)
|
||||
- FirebaseDynamicLinks (8.15.0):
|
||||
- FirebaseCore (~> 8.0)
|
||||
- FirebaseInstallations (8.15.0):
|
||||
- FirebaseCore (~> 8.0)
|
||||
- GoogleUtilities/Environment (~> 7.7)
|
||||
- GoogleUtilities/UserDefaults (~> 7.7)
|
||||
- PromisesObjC (< 3.0, >= 1.2)
|
||||
- fmt (6.2.1)
|
||||
- Giphy (2.1.20):
|
||||
- libwebp
|
||||
@@ -72,36 +82,52 @@ PODS:
|
||||
- Giphy (= 2.1.20)
|
||||
- React-Core
|
||||
- glog (0.3.5)
|
||||
- GoogleAppMeasurement (6.8.3):
|
||||
- GoogleUtilities/AppDelegateSwizzler (~> 6.7)
|
||||
- GoogleUtilities/MethodSwizzler (~> 6.7)
|
||||
- GoogleUtilities/Network (~> 6.7)
|
||||
- "GoogleUtilities/NSData+zlib (~> 6.7)"
|
||||
- nanopb (~> 1.30906.0)
|
||||
- GoogleDataTransport (7.5.1):
|
||||
- nanopb (~> 1.30906.0)
|
||||
- GoogleAppMeasurement (8.15.0):
|
||||
- GoogleAppMeasurement/AdIdSupport (= 8.15.0)
|
||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
|
||||
- GoogleUtilities/MethodSwizzler (~> 7.7)
|
||||
- GoogleUtilities/Network (~> 7.7)
|
||||
- "GoogleUtilities/NSData+zlib (~> 7.7)"
|
||||
- nanopb (~> 2.30908.0)
|
||||
- GoogleAppMeasurement/AdIdSupport (8.15.0):
|
||||
- GoogleAppMeasurement/WithoutAdIdSupport (= 8.15.0)
|
||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
|
||||
- GoogleUtilities/MethodSwizzler (~> 7.7)
|
||||
- GoogleUtilities/Network (~> 7.7)
|
||||
- "GoogleUtilities/NSData+zlib (~> 7.7)"
|
||||
- nanopb (~> 2.30908.0)
|
||||
- GoogleAppMeasurement/WithoutAdIdSupport (8.15.0):
|
||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.7)
|
||||
- GoogleUtilities/MethodSwizzler (~> 7.7)
|
||||
- GoogleUtilities/Network (~> 7.7)
|
||||
- "GoogleUtilities/NSData+zlib (~> 7.7)"
|
||||
- nanopb (~> 2.30908.0)
|
||||
- GoogleDataTransport (9.1.4):
|
||||
- GoogleUtilities/Environment (~> 7.7)
|
||||
- nanopb (< 2.30910.0, >= 2.30908.0)
|
||||
- PromisesObjC (< 3.0, >= 1.2)
|
||||
- GoogleSignIn (6.0.2):
|
||||
- AppAuth (~> 1.4)
|
||||
- GTMAppAuth (~> 1.0)
|
||||
- GTMSessionFetcher/Core (~> 1.1)
|
||||
- GoogleUtilities/AppDelegateSwizzler (6.7.2):
|
||||
- GoogleUtilities/AppDelegateSwizzler (7.7.0):
|
||||
- GoogleUtilities/Environment
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/Network
|
||||
- GoogleUtilities/Environment (6.7.2):
|
||||
- PromisesObjC (~> 1.2)
|
||||
- GoogleUtilities/Logger (6.7.2):
|
||||
- GoogleUtilities/Environment (7.7.0):
|
||||
- PromisesObjC (< 3.0, >= 1.2)
|
||||
- GoogleUtilities/Logger (7.7.0):
|
||||
- GoogleUtilities/Environment
|
||||
- GoogleUtilities/MethodSwizzler (6.7.2):
|
||||
- GoogleUtilities/MethodSwizzler (7.7.0):
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/Network (6.7.2):
|
||||
- GoogleUtilities/Network (7.7.0):
|
||||
- GoogleUtilities/Logger
|
||||
- "GoogleUtilities/NSData+zlib"
|
||||
- GoogleUtilities/Reachability
|
||||
- "GoogleUtilities/NSData+zlib (6.7.2)"
|
||||
- GoogleUtilities/Reachability (6.7.2):
|
||||
- "GoogleUtilities/NSData+zlib (7.7.0)"
|
||||
- GoogleUtilities/Reachability (7.7.0):
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/UserDefaults (6.7.2):
|
||||
- GoogleUtilities/UserDefaults (7.7.0):
|
||||
- GoogleUtilities/Logger
|
||||
- GTMAppAuth (1.2.2):
|
||||
- AppAuth/Core (~> 1.4)
|
||||
@@ -116,13 +142,13 @@ PODS:
|
||||
- libwebp/mux (1.2.1):
|
||||
- libwebp/demux
|
||||
- libwebp/webp (1.2.1)
|
||||
- nanopb (1.30906.0):
|
||||
- nanopb/decode (= 1.30906.0)
|
||||
- nanopb/encode (= 1.30906.0)
|
||||
- nanopb/decode (1.30906.0)
|
||||
- nanopb/encode (1.30906.0)
|
||||
- nanopb (2.30908.0):
|
||||
- nanopb/decode (= 2.30908.0)
|
||||
- nanopb/encode (= 2.30908.0)
|
||||
- nanopb/decode (2.30908.0)
|
||||
- nanopb/encode (2.30908.0)
|
||||
- ObjectiveDropboxOfficial (6.2.3)
|
||||
- PromisesObjC (1.2.12)
|
||||
- PromisesObjC (2.1.1)
|
||||
- RCT-Folly (2021.06.28.00-v2):
|
||||
- boost
|
||||
- DoubleConversion
|
||||
@@ -441,7 +467,7 @@ PODS:
|
||||
- React-Core
|
||||
- RNReanimated (1.13.4):
|
||||
- React-Core
|
||||
- RNScreens (3.10.1):
|
||||
- RNScreens (3.13.1):
|
||||
- React-Core
|
||||
- React-RCTImage
|
||||
- RNSound (0.11.1):
|
||||
@@ -462,9 +488,9 @@ DEPENDENCIES:
|
||||
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
||||
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
|
||||
- FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`)
|
||||
- Firebase/Analytics (~> 6.33.0)
|
||||
- Firebase/Crashlytics (~> 6.33.0)
|
||||
- Firebase/DynamicLinks (~> 6.33.0)
|
||||
- Firebase/Analytics (~> 8.0)
|
||||
- Firebase/Crashlytics (~> 8.0)
|
||||
- Firebase/DynamicLinks (~> 8.0)
|
||||
- "giphy-react-native-sdk (from `../node_modules/@giphy/react-native-sdk`)"
|
||||
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
|
||||
- ObjectiveDropboxOfficial (= 6.2.3)
|
||||
@@ -674,27 +700,27 @@ SPEC CHECKSUMS:
|
||||
DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662
|
||||
FBLazyVector: 2c76493a346ef8cacf1f442926a39f805fffec1f
|
||||
FBReactNativeSpec: 371350f24afa87b6aba606972ec959dcd4a95c9a
|
||||
Firebase: 8db6f2d1b2c5e2984efba4949a145875a8f65fe5
|
||||
FirebaseAnalytics: 5dd088bd2e67bb9d13dbf792d1164ceaf3052193
|
||||
FirebaseCore: d889d9e12535b7f36ac8bfbf1713a0836a3012cd
|
||||
FirebaseCoreDiagnostics: 770ac5958e1372ce67959ae4b4f31d8e127c3ac1
|
||||
FirebaseCrashlytics: 1a747c9cc084a24dc6d9511c991db1cd078154eb
|
||||
FirebaseDynamicLinks: 6eac37d86910382eafb6315d952cc44c9e176094
|
||||
FirebaseInstallations: 466c7b4d1f58fe16707693091da253726a731ed2
|
||||
Firebase: 5f8193dff4b5b7c5d5ef72ae54bb76c08e2b841d
|
||||
FirebaseAnalytics: 7761cbadb00a717d8d0939363eb46041526474fa
|
||||
FirebaseCore: 5743c5785c074a794d35f2fff7ecc254a91e08b1
|
||||
FirebaseCoreDiagnostics: 92e07a649aeb66352b319d43bdd2ee3942af84cb
|
||||
FirebaseCrashlytics: feb07e4e9187be3c23c6a846cce4824e5ce2dd0b
|
||||
FirebaseDynamicLinks: 1dc816ef789c5adac6fede0b46d11478175c70e4
|
||||
FirebaseInstallations: 40bd9054049b2eae9a2c38ef1c3dd213df3605cd
|
||||
fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9
|
||||
Giphy: b6d5087521d251bb8c99cdc0eb07bbdf86d142d5
|
||||
giphy-react-native-sdk: 7abccf2b52123a0f30ce99da895ab6288023680c
|
||||
glog: 476ee3e89abb49e07f822b48323c51c57124b572
|
||||
GoogleAppMeasurement: 966e88df9d19c15715137bb2ddaf52373f111436
|
||||
GoogleDataTransport: f56af7caa4ed338dc8e138a5d7c5973e66440833
|
||||
GoogleAppMeasurement: 4c19f031220c72464d460c9daa1fb5d1acce958e
|
||||
GoogleDataTransport: 5fffe35792f8b96ec8d6775f5eccd83c998d5a3b
|
||||
GoogleSignIn: fd381840dbe7c1137aa6dc30849a5c3e070c034a
|
||||
GoogleUtilities: 7f2f5a07f888cdb145101d6042bc4422f57e70b3
|
||||
GoogleUtilities: e0913149f6b0625b553d70dae12b49fc62914fd1
|
||||
GTMAppAuth: ad5c2b70b9a8689e1a04033c9369c4915bfcbe89
|
||||
GTMSessionFetcher: 43748f93435c2aa068b1cbe39655aaf600652e91
|
||||
libwebp: 98a37e597e40bfdb4c911fc98f2c53d0b12d05fc
|
||||
nanopb: 59317e09cf1f1a0af72f12af412d54edf52603fc
|
||||
nanopb: a0ba3315591a9ae0a16a309ee504766e90db0c96
|
||||
ObjectiveDropboxOfficial: fe206ce8c0bc49976c249d472db7fdbc53ebbd53
|
||||
PromisesObjC: 3113f7f76903778cf4a0586bd1ab89329a0b7b97
|
||||
PromisesObjC: ab77feca74fa2823e7af4249b8326368e61014cb
|
||||
RCT-Folly: 4d8508a426467c48885f1151029bc15fa5d7b3b8
|
||||
RCTRequired: 00581111c53531e39e3c6346ef0d2c0cf52a5a37
|
||||
RCTTypeSafety: 07e03ee7800e7dd65cba8e52ad0c2edb06c96604
|
||||
@@ -741,12 +767,12 @@ SPEC CHECKSUMS:
|
||||
RNGestureHandler: e5c7cab5f214503dcefd6b2b0cefb050e1f51c4a
|
||||
RNGoogleSignin: c4381751eefd73c552b923ba347a9bfc6f18771c
|
||||
RNReanimated: c1b56d030d1616239861534d9adb531f8cffab68
|
||||
RNScreens: 522705f2e5c9d27efb17f24aceb2bf8335bc7b8e
|
||||
RNScreens: 40a2cb40a02a609938137a1e0acfbf8fc9eebf19
|
||||
RNSound: 27e8268bdb0a1f191f219a33267f7e0445e8d62f
|
||||
RNSVG: ce9d996113475209013317e48b05c21ee988d42e
|
||||
RNWatch: 99637948ec9b5c9ec5a41920642594ad5ba07e80
|
||||
Yoga: 17cd9a50243093b547c1e539c749928dd68152da
|
||||
|
||||
PODFILE CHECKSUM: bef1335067eaa4e8c558b1248f8ab3948de855bc
|
||||
PODFILE CHECKSUM: 0e8826a5cb9ee147354a83321ecb3104132f510b
|
||||
|
||||
COCOAPODS: 1.11.3
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>22.3.0</string>
|
||||
<string>99.0.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>NSExtension</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>22.3.0</string>
|
||||
<string>99.0.0</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>22.3.0</string>
|
||||
<string>99.0.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>22.3.0</string>
|
||||
<string>99.0.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>CLKComplicationPrincipalClass</key>
|
||||
|
||||
@@ -24,8 +24,14 @@
|
||||
0BD906EA1EC0C00300C8C18E /* JitsiMeet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0BD906E81EC0C00300C8C18E /* JitsiMeet.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
4E51B76425E5345E0038575A /* ScheenshareEventEmiter.h in Headers */ = {isa = PBXBuildFile; fileRef = 4E51B76225E5345E0038575A /* ScheenshareEventEmiter.h */; };
|
||||
4E51B76525E5345E0038575A /* ScheenshareEventEmiter.m in Sources */ = {isa = PBXBuildFile; fileRef = 4E51B76325E5345E0038575A /* ScheenshareEventEmiter.m */; };
|
||||
4EBA6E61286072E300B31882 /* JitsiMeetViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 4EBA6E5F286072E300B31882 /* JitsiMeetViewController.h */; };
|
||||
4EBA6E62286072E300B31882 /* JitsiMeetViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EBA6E60286072E300B31882 /* JitsiMeetViewController.m */; };
|
||||
4EBA6E652860B1E800B31882 /* JitsiMeetRenderingView.h in Headers */ = {isa = PBXBuildFile; fileRef = 4EBA6E632860B1E800B31882 /* JitsiMeetRenderingView.h */; };
|
||||
4EBA6E662860B1E800B31882 /* JitsiMeetRenderingView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EBA6E642860B1E800B31882 /* JitsiMeetRenderingView.m */; };
|
||||
4ED4FFF32721B9B90074E620 /* JitsiAudioSession.h in Headers */ = {isa = PBXBuildFile; fileRef = 4ED4FFF12721B9B90074E620 /* JitsiAudioSession.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
4ED4FFF42721B9B90074E620 /* JitsiAudioSession.m in Sources */ = {isa = PBXBuildFile; fileRef = 4ED4FFF22721B9B90074E620 /* JitsiAudioSession.m */; };
|
||||
4EEC9630286C73A2008705FA /* JitsiMeetView+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = 4EEC962E286C73A2008705FA /* JitsiMeetView+Private.h */; };
|
||||
4EEC9631286C73A2008705FA /* JitsiMeetView+Private.m in Sources */ = {isa = PBXBuildFile; fileRef = 4EEC962F286C73A2008705FA /* JitsiMeetView+Private.m */; };
|
||||
6F08DF7D4458EE3CF3F36F6D /* libPods-JitsiMeetSDK.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E4376CA6886DE68FD7A4294B /* libPods-JitsiMeetSDK.a */; };
|
||||
A4A934E9212F3ADB001E9388 /* Dropbox.m in Sources */ = {isa = PBXBuildFile; fileRef = A4A934E8212F3ADB001E9388 /* Dropbox.m */; };
|
||||
C6245F5D2053091D0040BE68 /* image-resize@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C6245F5B2053091D0040BE68 /* image-resize@2x.png */; };
|
||||
@@ -79,9 +85,15 @@
|
||||
0BD906E91EC0C00300C8C18E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
4E51B76225E5345E0038575A /* ScheenshareEventEmiter.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ScheenshareEventEmiter.h; sourceTree = "<group>"; };
|
||||
4E51B76325E5345E0038575A /* ScheenshareEventEmiter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ScheenshareEventEmiter.m; sourceTree = "<group>"; };
|
||||
4EBA6E5F286072E300B31882 /* JitsiMeetViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetViewController.h; sourceTree = "<group>"; };
|
||||
4EBA6E60286072E300B31882 /* JitsiMeetViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeetViewController.m; sourceTree = "<group>"; };
|
||||
4EBA6E632860B1E800B31882 /* JitsiMeetRenderingView.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetRenderingView.h; sourceTree = "<group>"; };
|
||||
4EBA6E642860B1E800B31882 /* JitsiMeetRenderingView.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeetRenderingView.m; sourceTree = "<group>"; };
|
||||
4ED4FFF12721B9B90074E620 /* JitsiAudioSession.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiAudioSession.h; sourceTree = "<group>"; };
|
||||
4ED4FFF22721B9B90074E620 /* JitsiAudioSession.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiAudioSession.m; sourceTree = "<group>"; };
|
||||
4ED4FFF52721BAE10074E620 /* JitsiAudioSession+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiAudioSession+Private.h"; sourceTree = "<group>"; };
|
||||
4EEC962E286C73A2008705FA /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
|
||||
4EEC962F286C73A2008705FA /* JitsiMeetView+Private.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "JitsiMeetView+Private.m"; sourceTree = "<group>"; };
|
||||
891FE43DAD30BC8976683100 /* Pods-JitsiMeetSDK.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeetSDK.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeetSDK/Pods-JitsiMeetSDK.release.xcconfig"; sourceTree = "<group>"; };
|
||||
98E09B5C73D9036B4ED252FC /* Pods-JitsiMeet.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.debug.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
9C77CA3CC919B081F1A52982 /* Pods-JitsiMeet.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JitsiMeet.release.xcconfig"; path = "../Pods/Target Support Files/Pods-JitsiMeet/Pods-JitsiMeet.release.xcconfig"; sourceTree = "<group>"; };
|
||||
@@ -94,7 +106,6 @@
|
||||
C69EFA0B209A0F660027712B /* JMCallKitListener.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JMCallKitListener.swift; sourceTree = "<group>"; };
|
||||
C6A3425E204EF76800E062DD /* DragGestureController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DragGestureController.swift; sourceTree = "<group>"; };
|
||||
C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiPViewCoordinator.swift; sourceTree = "<group>"; };
|
||||
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
|
||||
C81E9AB825AC5AD800B134D9 /* ExternalAPI.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ExternalAPI.h; sourceTree = "<group>"; };
|
||||
C8AFD27D2462C613000293D2 /* InfoPlistUtil.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = InfoPlistUtil.h; sourceTree = "<group>"; };
|
||||
C8AFD27E2462C613000293D2 /* InfoPlistUtil.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = InfoPlistUtil.m; sourceTree = "<group>"; };
|
||||
@@ -194,12 +205,17 @@
|
||||
DE65AACB2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h */,
|
||||
DE81A2DD2317ED5400AE1940 /* JitsiMeetBaseLogHandler.m */,
|
||||
0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
|
||||
4EEC962E286C73A2008705FA /* JitsiMeetView+Private.h */,
|
||||
4EEC962F286C73A2008705FA /* JitsiMeetView+Private.m */,
|
||||
0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
|
||||
4EBA6E632860B1E800B31882 /* JitsiMeetRenderingView.h */,
|
||||
4EBA6E642860B1E800B31882 /* JitsiMeetRenderingView.m */,
|
||||
4EBA6E5F286072E300B31882 /* JitsiMeetViewController.h */,
|
||||
4EBA6E60286072E300B31882 /* JitsiMeetViewController.m */,
|
||||
DE81A2D72316AC7600AE1940 /* LogBridge.m */,
|
||||
DE65AAC92317FFCD00290BEC /* LogUtils.h */,
|
||||
DEAFA777229EAD3B0033A7FA /* RNRootView.h */,
|
||||
DEAFA778229EAD520033A7FA /* RNRootView.m */,
|
||||
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */,
|
||||
0B412F1B1EDEC80100B1A0A6 /* JitsiMeetViewDelegate.h */,
|
||||
DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */,
|
||||
C6A3426B204F127900E062DD /* picture-in-picture */,
|
||||
@@ -284,11 +300,14 @@
|
||||
DEA9F284258A5D9900D4CD74 /* JitsiMeetSDK.h in Headers */,
|
||||
4E51B76425E5345E0038575A /* ScheenshareEventEmiter.h in Headers */,
|
||||
DE65AACC2318028300290BEC /* JitsiMeetBaseLogHandler+Private.h in Headers */,
|
||||
4EBA6E652860B1E800B31882 /* JitsiMeetRenderingView.h in Headers */,
|
||||
0B412F221EDEF6EA00B1A0A6 /* JitsiMeetViewDelegate.h in Headers */,
|
||||
4ED4FFF32721B9B90074E620 /* JitsiAudioSession.h in Headers */,
|
||||
4EEC9630286C73A2008705FA /* JitsiMeetView+Private.h in Headers */,
|
||||
0BD906EA1EC0C00300C8C18E /* JitsiMeet.h in Headers */,
|
||||
DE81A2D42316AC4D00AE1940 /* JitsiMeetLogger.h in Headers */,
|
||||
DE65AACA2317FFCD00290BEC /* LogUtils.h in Headers */,
|
||||
4EBA6E61286072E300B31882 /* JitsiMeetViewController.h in Headers */,
|
||||
DEAD3226220C497000E93636 /* JitsiMeetConferenceOptions.h in Headers */,
|
||||
C81E9AB925AC5AD800B134D9 /* ExternalAPI.h in Headers */,
|
||||
C8AFD27F2462C613000293D2 /* InfoPlistUtil.h in Headers */,
|
||||
@@ -449,6 +468,7 @@
|
||||
files = (
|
||||
0BB9AD7B1F5EC8F4001C08DB /* CallKit.m in Sources */,
|
||||
DE81A2DF2317ED5400AE1940 /* JitsiMeetBaseLogHandler.m in Sources */,
|
||||
4EBA6E662860B1E800B31882 /* JitsiMeetRenderingView.m in Sources */,
|
||||
4ED4FFF42721B9B90074E620 /* JitsiAudioSession.m in Sources */,
|
||||
0BB9AD7D1F60356D001C08DB /* AppInfo.m in Sources */,
|
||||
DE81A2D92316AC7600AE1940 /* LogBridge.m in Sources */,
|
||||
@@ -465,9 +485,11 @@
|
||||
0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
|
||||
C69EFA0C209A0F660027712B /* JMCallKitEmitter.swift in Sources */,
|
||||
DEFE535621FB2E8300011A3A /* ReactUtils.m in Sources */,
|
||||
4EEC9631286C73A2008705FA /* JitsiMeetView+Private.m in Sources */,
|
||||
C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */,
|
||||
4E51B76525E5345E0038575A /* ScheenshareEventEmiter.m in Sources */,
|
||||
A4A934E9212F3ADB001E9388 /* Dropbox.m in Sources */,
|
||||
4EBA6E62286072E300B31882 /* JitsiMeetViewController.m in Sources */,
|
||||
C69EFA0D209A0F660027712B /* JMCallKitProxy.swift in Sources */,
|
||||
DE81A2D52316AC4D00AE1940 /* JitsiMeetLogger.m in Sources */,
|
||||
C69EFA0E209A0F660027712B /* JMCallKitListener.swift in Sources */,
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTEventEmitter.h>
|
||||
|
||||
static NSString * const sendEventNotificationName = @"org.jitsi.meet.SendEvent";
|
||||
|
||||
@interface ExternalAPI : RCTEventEmitter<RCTBridgeModule>
|
||||
|
||||
- (void)sendHangUp;
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
*/
|
||||
|
||||
#import "ExternalAPI.h"
|
||||
#import "JitsiMeetView+Private.h"
|
||||
|
||||
// Events
|
||||
static NSString * const hangUpAction = @"org.jitsi.meet.HANG_UP";
|
||||
@@ -89,33 +88,15 @@ RCT_EXPORT_MODULE();
|
||||
* @param scope
|
||||
*/
|
||||
RCT_EXPORT_METHOD(sendEvent:(NSString *)name
|
||||
data:(NSDictionary *)data
|
||||
scope:(NSString *)scope) {
|
||||
// The JavaScript App needs to provide uniquely identifying information to
|
||||
// the native ExternalAPI module so that the latter may match the former
|
||||
// to the native JitsiMeetView which hosts it.
|
||||
JitsiMeetView *view = [JitsiMeetView viewForExternalAPIScope:scope];
|
||||
|
||||
if (!view) {
|
||||
return;
|
||||
}
|
||||
|
||||
id delegate = view.delegate;
|
||||
|
||||
if (!delegate) {
|
||||
return;
|
||||
}
|
||||
|
||||
data:(NSDictionary *)data) {
|
||||
if ([name isEqual: @"PARTICIPANTS_INFO_RETRIEVED"]) {
|
||||
[self onParticipantsInfoRetrieved: data];
|
||||
return;
|
||||
}
|
||||
|
||||
SEL sel = NSSelectorFromString([self methodNameFromEventName:name]);
|
||||
|
||||
if (sel && [delegate respondsToSelector:sel]) {
|
||||
[delegate performSelector:sel withObject:data];
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:sendEventNotificationName
|
||||
object:nil
|
||||
userInfo:@{@"name": name, @"data": data}];
|
||||
}
|
||||
|
||||
- (void) onParticipantsInfoRetrieved:(NSDictionary *)data {
|
||||
@@ -127,28 +108,6 @@ RCT_EXPORT_METHOD(sendEvent:(NSString *)name
|
||||
[participantInfoCompletionHandlers removeObjectForKey:completionHandlerId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a specific event name i.e. redux action type description to a
|
||||
* method name.
|
||||
*
|
||||
* @param eventName The event name to convert to a method name.
|
||||
* @return A method name constructed from the specified `eventName`.
|
||||
*/
|
||||
- (NSString *)methodNameFromEventName:(NSString *)eventName {
|
||||
NSMutableString *methodName
|
||||
= [NSMutableString stringWithCapacity:eventName.length];
|
||||
|
||||
for (NSString *c in [eventName componentsSeparatedByString:@"_"]) {
|
||||
if (c.length) {
|
||||
[methodName appendString:
|
||||
methodName.length ? c.capitalizedString : c.lowercaseString];
|
||||
}
|
||||
}
|
||||
[methodName appendString:@":"];
|
||||
|
||||
return methodName;
|
||||
}
|
||||
|
||||
- (void)sendHangUp {
|
||||
[self sendEventWithName:hangUpAction body:nil];
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>FMWK</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>5.2.0</string>
|
||||
<string>99.0.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||
<key>NSPrincipalClass</key>
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
|
||||
#import <Intents/Intents.h>
|
||||
#import <RNGoogleSignin/RNGoogleSignin.h>
|
||||
#import <WebRTC/RTCLogging.h>
|
||||
|
||||
#import "Dropbox.h"
|
||||
#import "JitsiMeet+Private.h"
|
||||
@@ -25,9 +27,6 @@
|
||||
#import "RNSplashScreen.h"
|
||||
#import "ScheenshareEventEmiter.h"
|
||||
|
||||
#import <RNGoogleSignin/RNGoogleSignin.h>
|
||||
#import <WebRTC/RTCLogging.h>
|
||||
|
||||
@implementation JitsiMeet {
|
||||
RCTBridgeWrapper *_bridgeWrapper;
|
||||
NSDictionary *_launchOptions;
|
||||
@@ -87,8 +86,12 @@
|
||||
restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> *))restorationHandler {
|
||||
|
||||
JitsiMeetConferenceOptions *options = [self optionsFromUserActivity:userActivity];
|
||||
if (options) {
|
||||
[JitsiMeetView updateProps:[options asProps]];
|
||||
return true;
|
||||
}
|
||||
|
||||
return options && [JitsiMeetView setPropsInViews:[options asProps]];
|
||||
return false;
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)app
|
||||
@@ -112,8 +115,9 @@
|
||||
JitsiMeetConferenceOptions *conferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
|
||||
builder.room = [url absoluteString];
|
||||
}];
|
||||
[JitsiMeetView updateProps:[conferenceOptions asProps]];
|
||||
|
||||
return [JitsiMeetView setPropsInViews:[conferenceOptions asProps]];
|
||||
return true;
|
||||
}
|
||||
|
||||
#pragma mark - Utility methods
|
||||
|
||||
30
ios/sdk/src/JitsiMeetRenderingView.h
Normal file
30
ios/sdk/src/JitsiMeetRenderingView.h
Normal file
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright @ 2022-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "JitsiMeetViewDelegate.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface JitsiMeetRenderingView : UIView
|
||||
|
||||
@property (nonatomic, assign) BOOL isPiPEnabled;
|
||||
|
||||
- (void)setProps:(NSDictionary *_Nonnull)newProps;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
83
ios/sdk/src/JitsiMeetRenderingView.m
Normal file
83
ios/sdk/src/JitsiMeetRenderingView.m
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright @ 2022-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <mach/mach_time.h>
|
||||
|
||||
#import "JitsiMeetRenderingView.h"
|
||||
#import "ReactUtils.h"
|
||||
#import "RNRootView.h"
|
||||
#import "JitsiMeet+Private.h"
|
||||
|
||||
/**
|
||||
* Backwards compatibility: turn the boolean prop into a feature flag.
|
||||
*/
|
||||
static NSString *const PiPEnabledFeatureFlag = @"pip.enabled";
|
||||
|
||||
@interface JitsiMeetRenderingView ()
|
||||
@end
|
||||
|
||||
@implementation JitsiMeetRenderingView {
|
||||
/**
|
||||
* React Native view where the entire content will be rendered.
|
||||
*/
|
||||
RNRootView *rootView;
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes the given props to the React Native application. The props which we pass
|
||||
* are a combination of 3 different sources:
|
||||
*
|
||||
* - JitsiMeet.defaultConferenceOptions
|
||||
* - This function's parameters
|
||||
* - Some extras which are added by this function
|
||||
*/
|
||||
- (void)setProps:(NSDictionary *_Nonnull)newProps {
|
||||
NSMutableDictionary *props = mergeProps([[JitsiMeet sharedInstance] getDefaultProps], newProps);
|
||||
|
||||
// Set the PiP flag if it wasn't manually set.
|
||||
NSMutableDictionary *featureFlags = props[@"flags"];
|
||||
// TODO: temporary implementation
|
||||
if (featureFlags[PiPEnabledFeatureFlag] == nil) {
|
||||
featureFlags[PiPEnabledFeatureFlag] = @(self.isPiPEnabled);
|
||||
}
|
||||
|
||||
// This method is supposed to be imperative i.e. a second
|
||||
// invocation with one and the same URL is expected to join the respective
|
||||
// conference again if the first invocation was followed by leaving the
|
||||
// conference. However, React and, respectively,
|
||||
// appProperties/initialProperties are declarative expressions i.e. one and
|
||||
// the same URL will not trigger an automatic re-render in the JavaScript
|
||||
// source code. The workaround implemented below introduces imperativeness
|
||||
// in React Component props by defining a unique value per invocation.
|
||||
props[@"timestamp"] = @(mach_absolute_time());
|
||||
|
||||
if (rootView) {
|
||||
// Update props with the new URL.
|
||||
rootView.appProperties = props;
|
||||
} else {
|
||||
RCTBridge *bridge = [[JitsiMeet sharedInstance] getReactBridge];
|
||||
rootView = [[RNRootView alloc] initWithBridge:bridge
|
||||
moduleName:@"App"
|
||||
initialProperties:props];
|
||||
rootView.backgroundColor = self.backgroundColor;
|
||||
|
||||
// Add rootView as a subview which completely covers this one.
|
||||
[rootView setFrame:[self bounds]];
|
||||
rootView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
[self addSubview:rootView];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,6 +1,5 @@
|
||||
/*
|
||||
* Copyright @ 2018-present 8x8, Inc.
|
||||
* Copyright @ 2017-2018 Atlassian Pty Ltd
|
||||
* Copyright @ 2022-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,11 +14,16 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "JitsiMeetView.h"
|
||||
#import <JitsiMeetSDK/JitsiMeetSDK.h>
|
||||
|
||||
@interface JitsiMeetView ()
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
+ (instancetype _Nullable)viewForExternalAPIScope:(NSString *_Nonnull)externalAPIScope;
|
||||
+ (BOOL)setPropsInViews:(NSDictionary *_Nonnull)newProps;
|
||||
static NSString * const updateViewPropsNotificationName = @"org.jitsi.meet.UpdateViewProps";
|
||||
|
||||
@interface JitsiMeetView (Private)
|
||||
|
||||
+ (void)updateProps:(NSDictionary *_Nonnull)newProps;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
|
||||
25
ios/sdk/src/JitsiMeetView+Private.m
Normal file
25
ios/sdk/src/JitsiMeetView+Private.m
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright @ 2022-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "JitsiMeetView+Private.h"
|
||||
|
||||
@implementation JitsiMeetView (Private)
|
||||
|
||||
+ (void)updateProps:(NSDictionary *_Nonnull)newProps {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:updateViewPropsNotificationName object:nil userInfo:@{@"props": newProps}];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -20,43 +20,22 @@
|
||||
#import "ExternalAPI.h"
|
||||
#import "JitsiMeet+Private.h"
|
||||
#import "JitsiMeetConferenceOptions+Private.h"
|
||||
#import "JitsiMeetView+Private.h"
|
||||
#import "JitsiMeetView.h"
|
||||
#import "JitsiMeetViewController.h"
|
||||
#import "ReactUtils.h"
|
||||
#import "RNRootView.h"
|
||||
|
||||
@interface JitsiMeetView ()
|
||||
|
||||
/**
|
||||
* Backwards compatibility: turn the boolean prop into a feature flag.
|
||||
*/
|
||||
static NSString *const PiPEnabledFeatureFlag = @"pip.enabled";
|
||||
@property (nonatomic, strong) JitsiMeetViewController *jitsiMeetViewController;
|
||||
@property (nonatomic, strong) UINavigationController *navController;
|
||||
@property (nonatomic, readonly) BOOL isPiPEnabled;
|
||||
|
||||
@end
|
||||
|
||||
@implementation JitsiMeetView {
|
||||
/**
|
||||
* The unique identifier of this `JitsiMeetView` within the process for the
|
||||
* purposes of `ExternalAPI`. The name scope was inspired by postis which we
|
||||
* use on Web for the similar purposes of the iframe-based external API.
|
||||
*/
|
||||
NSString *externalAPIScope;
|
||||
@implementation JitsiMeetView
|
||||
|
||||
/**
|
||||
* React Native view where the entire content will be rendered.
|
||||
*/
|
||||
RNRootView *rootView;
|
||||
}
|
||||
|
||||
/**
|
||||
* The `JitsiMeetView`s associated with their `ExternalAPI` scopes (i.e. unique
|
||||
* identifiers within the process).
|
||||
*/
|
||||
static NSMapTable<NSString *, JitsiMeetView *> *views;
|
||||
/**
|
||||
* This gets called automagically when the program starts.
|
||||
*/
|
||||
__attribute__((constructor))
|
||||
static void initializeViewsMap() {
|
||||
views = [NSMapTable strongToWeakObjectsMapTable];
|
||||
}
|
||||
@dynamic isPiPEnabled;
|
||||
|
||||
#pragma mark Initializers
|
||||
|
||||
@@ -87,6 +66,10 @@ static void initializeViewsMap() {
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal initialization:
|
||||
*
|
||||
@@ -94,70 +77,57 @@ static void initializeViewsMap() {
|
||||
* - initializes the external API scope
|
||||
*/
|
||||
- (void)initWithXXX {
|
||||
// Hook this JitsiMeetView into ExternalAPI.
|
||||
externalAPIScope = [NSUUID UUID].UUIDString;
|
||||
[views setObject:self forKey:externalAPIScope];
|
||||
|
||||
// Set a background color which is in accord with the JavaScript and Android
|
||||
// parts of the application and causes less perceived visual flicker than
|
||||
// the default background color.
|
||||
self.backgroundColor
|
||||
= [UIColor colorWithRed:.07f green:.07f blue:.07f alpha:1];
|
||||
self.jitsiMeetViewController = [[JitsiMeetViewController alloc] init];
|
||||
self.jitsiMeetViewController.view.frame = [self bounds];
|
||||
[self addSubview:self.jitsiMeetViewController.view];
|
||||
|
||||
[self registerObservers];
|
||||
}
|
||||
|
||||
#pragma mark API
|
||||
|
||||
- (void)join:(JitsiMeetConferenceOptions *)options {
|
||||
[self setProps:options == nil ? @{} : [options asProps]];
|
||||
[self.jitsiMeetViewController join:options withPiP:self.isPiPEnabled];
|
||||
}
|
||||
|
||||
- (void)leave {
|
||||
[self setProps:@{}];
|
||||
[self.jitsiMeetViewController leave];
|
||||
}
|
||||
|
||||
- (void)hangUp {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI sendHangUp];
|
||||
[self.jitsiMeetViewController hangUp];
|
||||
}
|
||||
|
||||
- (void)setAudioMuted:(BOOL)muted {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI sendSetAudioMuted:muted];
|
||||
[self.jitsiMeetViewController setAudioMuted:muted];
|
||||
}
|
||||
|
||||
- (void)sendEndpointTextMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI sendEndpointTextMessage:message :to];
|
||||
[self.jitsiMeetViewController sendEndpointTextMessage:message :to];
|
||||
}
|
||||
|
||||
- (void)toggleScreenShare:(BOOL)enabled {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI toggleScreenShare:enabled];
|
||||
[self.jitsiMeetViewController toggleScreenShare:enabled];
|
||||
}
|
||||
|
||||
- (void)retrieveParticipantsInfo:(void (^ _Nonnull)(NSArray * _Nullable))completionHandler {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI retrieveParticipantsInfo:completionHandler];
|
||||
[self.jitsiMeetViewController retrieveParticipantsInfo:completionHandler];
|
||||
}
|
||||
|
||||
- (void)openChat:(NSString*)to {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI openChat:to];
|
||||
[self.jitsiMeetViewController openChat:to];
|
||||
}
|
||||
|
||||
- (void)closeChat {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI closeChat];
|
||||
[self.jitsiMeetViewController closeChat];
|
||||
}
|
||||
|
||||
- (void)sendChatMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI sendChatMessage:message :to];
|
||||
[self.jitsiMeetViewController sendChatMessage:message :to];
|
||||
}
|
||||
|
||||
- (void)setVideoMuted:(BOOL)muted {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI sendSetVideoMuted:muted];
|
||||
[self.jitsiMeetViewController setVideoMuted:muted];
|
||||
}
|
||||
|
||||
- (void)setClosedCaptionsEnabled:(BOOL)enabled {
|
||||
@@ -165,79 +135,47 @@ static void initializeViewsMap() {
|
||||
[externalAPI sendSetClosedCaptionsEnabled:enabled];
|
||||
}
|
||||
|
||||
#pragma mark Private methods
|
||||
#pragma mark Private
|
||||
|
||||
- (BOOL)isPiPEnabled {
|
||||
return self.delegate && [self.delegate respondsToSelector:@selector(enterPictureInPicture:)];
|
||||
}
|
||||
|
||||
- (void)registerObservers {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleSendEventNotification:) name:sendEventNotificationName object:nil];
|
||||
}
|
||||
|
||||
- (void)handleSendEventNotification:(NSNotification *)notification {
|
||||
NSString *eventName = notification.userInfo[@"name"];
|
||||
NSString *eventData = notification.userInfo[@"data"];
|
||||
|
||||
SEL sel = NSSelectorFromString([self methodNameFromEventName:eventName]);
|
||||
|
||||
if (sel && [self.delegate respondsToSelector:sel]) {
|
||||
[self.delegate performSelector:sel withObject:eventData];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Passes the given props to the React Native application. The props which we pass
|
||||
* are a combination of 3 different sources:
|
||||
* Converts a specific event name i.e. redux action type description to a
|
||||
* method name.
|
||||
*
|
||||
* - JitsiMeet.defaultConferenceOptions
|
||||
* - This function's parameters
|
||||
* - Some extras which are added by this function
|
||||
* @param eventName The event name to convert to a method name.
|
||||
* @return A method name constructed from the specified `eventName`.
|
||||
*/
|
||||
- (void)setProps:(NSDictionary *_Nonnull)newProps {
|
||||
NSMutableDictionary *props = mergeProps([[JitsiMeet sharedInstance] getDefaultProps], newProps);
|
||||
- (NSString *)methodNameFromEventName:(NSString *)eventName {
|
||||
NSMutableString *methodName
|
||||
= [NSMutableString stringWithCapacity:eventName.length];
|
||||
|
||||
// Set the PiP flag if it wasn't manually set.
|
||||
NSMutableDictionary *featureFlags = props[@"flags"];
|
||||
if (featureFlags[PiPEnabledFeatureFlag] == nil) {
|
||||
featureFlags[PiPEnabledFeatureFlag]
|
||||
= [NSNumber numberWithBool:
|
||||
self.delegate && [self.delegate respondsToSelector:@selector(enterPictureInPicture:)]];
|
||||
}
|
||||
for (NSString *c in [eventName componentsSeparatedByString:@"_"]) {
|
||||
if (c.length) {
|
||||
[methodName appendString:
|
||||
methodName.length ? c.capitalizedString : c.lowercaseString];
|
||||
}
|
||||
}
|
||||
[methodName appendString:@":"];
|
||||
|
||||
props[@"externalAPIScope"] = externalAPIScope;
|
||||
|
||||
// This method is supposed to be imperative i.e. a second
|
||||
// invocation with one and the same URL is expected to join the respective
|
||||
// conference again if the first invocation was followed by leaving the
|
||||
// conference. However, React and, respectively,
|
||||
// appProperties/initialProperties are declarative expressions i.e. one and
|
||||
// the same URL will not trigger an automatic re-render in the JavaScript
|
||||
// source code. The workaround implemented below introduces imperativeness
|
||||
// in React Component props by defining a unique value per invocation.
|
||||
props[@"timestamp"] = @(mach_absolute_time());
|
||||
|
||||
if (rootView) {
|
||||
// Update props with the new URL.
|
||||
rootView.appProperties = props;
|
||||
} else {
|
||||
RCTBridge *bridge = [[JitsiMeet sharedInstance] getReactBridge];
|
||||
rootView
|
||||
= [[RNRootView alloc] initWithBridge:bridge
|
||||
moduleName:@"App"
|
||||
initialProperties:props];
|
||||
rootView.backgroundColor = self.backgroundColor;
|
||||
|
||||
// Add rootView as a subview which completely covers this one.
|
||||
[rootView setFrame:[self bounds]];
|
||||
rootView.autoresizingMask
|
||||
= UIViewAutoresizingFlexibleWidth
|
||||
| UIViewAutoresizingFlexibleHeight;
|
||||
[self addSubview:rootView];
|
||||
}
|
||||
}
|
||||
|
||||
+ (BOOL)setPropsInViews:(NSDictionary *_Nonnull)newProps {
|
||||
BOOL handled = NO;
|
||||
|
||||
if (views) {
|
||||
for (NSString *externalAPIScope in views) {
|
||||
JitsiMeetView *view
|
||||
= [self viewForExternalAPIScope:externalAPIScope];
|
||||
|
||||
if (view) {
|
||||
[view setProps:newProps];
|
||||
handled = YES;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
+ (instancetype)viewForExternalAPIScope:(NSString *)externalAPIScope {
|
||||
return [views objectForKey:externalAPIScope];
|
||||
return methodName;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
38
ios/sdk/src/JitsiMeetViewController.h
Normal file
38
ios/sdk/src/JitsiMeetViewController.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright @ 2022-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import "JitsiMeetConferenceOptions.h"
|
||||
|
||||
NS_ASSUME_NONNULL_BEGIN
|
||||
|
||||
@interface JitsiMeetViewController : UIViewController
|
||||
|
||||
- (void)join:(JitsiMeetConferenceOptions *)options withPiP:(BOOL)enablePiP;
|
||||
- (void)leave;
|
||||
- (void)hangUp;
|
||||
- (void)setAudioMuted:(BOOL)muted;
|
||||
- (void)sendEndpointTextMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to;
|
||||
- (void)toggleScreenShare:(BOOL)enabled;
|
||||
- (void)retrieveParticipantsInfo:(void (^ _Nonnull)(NSArray * _Nullable))completionHandler;
|
||||
- (void)openChat:(NSString*)to;
|
||||
- (void)closeChat;
|
||||
- (void)sendChatMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to;
|
||||
- (void)setVideoMuted:(BOOL)muted;
|
||||
|
||||
@end
|
||||
|
||||
NS_ASSUME_NONNULL_END
|
||||
127
ios/sdk/src/JitsiMeetViewController.m
Normal file
127
ios/sdk/src/JitsiMeetViewController.m
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* Copyright @ 2022-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "JitsiMeetViewController.h"
|
||||
#import "JitsiMeet+Private.h"
|
||||
#import "JitsiMeetConferenceOptions+Private.h"
|
||||
#import "JitsiMeetRenderingView.h"
|
||||
#import "JitsiMeetView+Private.h"
|
||||
|
||||
@interface JitsiMeetViewController ()
|
||||
|
||||
@property (strong, nonatomic) JitsiMeetRenderingView *view;
|
||||
|
||||
@end
|
||||
|
||||
@implementation JitsiMeetViewController
|
||||
|
||||
@dynamic view;
|
||||
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
[self registerObservers];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc {
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
}
|
||||
|
||||
- (void)loadView {
|
||||
[super loadView];
|
||||
|
||||
self.view = [[JitsiMeetRenderingView alloc] init];
|
||||
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
// Set a background color which is in accord with the JavaScript and Android
|
||||
// parts of the application and causes less perceived visual flicker than
|
||||
// the default background color.
|
||||
self.view.backgroundColor = [UIColor colorWithRed:.07f green:.07f blue:.07f alpha:1];
|
||||
}
|
||||
|
||||
- (void)join:(JitsiMeetConferenceOptions *)options withPiP:(BOOL)enablePiP {
|
||||
self.view.isPiPEnabled = enablePiP;
|
||||
[self.view setProps:options == nil ? @{} : [options asProps]];
|
||||
}
|
||||
|
||||
- (void)leave {
|
||||
[self.view setProps:@{}];
|
||||
}
|
||||
|
||||
- (void)hangUp {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI sendHangUp];
|
||||
}
|
||||
|
||||
- (void)setAudioMuted:(BOOL)muted {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI sendSetAudioMuted:muted];
|
||||
}
|
||||
|
||||
- (void)sendEndpointTextMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI sendEndpointTextMessage:message :to];
|
||||
}
|
||||
|
||||
- (void)toggleScreenShare:(BOOL)enabled {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI toggleScreenShare:enabled];
|
||||
}
|
||||
|
||||
- (void)retrieveParticipantsInfo:(void (^ _Nonnull)(NSArray * _Nullable))completionHandler {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI retrieveParticipantsInfo:completionHandler];
|
||||
}
|
||||
|
||||
- (void)openChat:(NSString*)to {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI openChat:to];
|
||||
}
|
||||
|
||||
- (void)closeChat {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI closeChat];
|
||||
}
|
||||
|
||||
- (void)sendChatMessage:(NSString * _Nonnull)message :(NSString * _Nullable)to {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI sendChatMessage:message :to];
|
||||
}
|
||||
|
||||
- (void)setVideoMuted:(BOOL)muted {
|
||||
ExternalAPI *externalAPI = [[JitsiMeet sharedInstance] getExternalAPI];
|
||||
[externalAPI sendSetVideoMuted:muted];
|
||||
}
|
||||
|
||||
#pragma mark Private
|
||||
|
||||
- (void)registerObservers {
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleUpdateViewPropsNotification:) name:updateViewPropsNotificationName object:nil];
|
||||
}
|
||||
|
||||
- (void)handleUpdateViewPropsNotification:(NSNotification *)notification {
|
||||
NSDictionary *props = [notification.userInfo objectForKey:@"props"];
|
||||
[self.view setProps:props];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -17,6 +17,7 @@
|
||||
"fr": "Französisch",
|
||||
"frCA": "Französisch (Kanada)",
|
||||
"hr": "Kroatisch",
|
||||
"hsb": "Obersorbisch",
|
||||
"hu": "Ungarisch",
|
||||
"hy": "Armenisch",
|
||||
"it": "Italienisch",
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
"fr": "French",
|
||||
"frCA": "French (Canadian)",
|
||||
"hr": "Croatian",
|
||||
"hsb": "Upper Sorbian",
|
||||
"hu": "Hungarian",
|
||||
"hy": "Armenian",
|
||||
"it": "Italian",
|
||||
|
||||
47
lang/languages-hsb.json
Normal file
47
lang/languages-hsb.json
Normal file
@@ -0,0 +1,47 @@
|
||||
{
|
||||
"ar": "arabšćina",
|
||||
"bg": "bołharšćina",
|
||||
"cs": "čěšćina",
|
||||
"da": "danšćina",
|
||||
"de": "němčina",
|
||||
"el": "grjekšćina",
|
||||
"en": "jendźelšćina",
|
||||
"enGB": "jendźelšćina (Wulka Britaniska)",
|
||||
"eo": "esperanto",
|
||||
"es": "španišćina",
|
||||
"esUS": "španišćina (Łaćonska)",
|
||||
"et": "estišćina",
|
||||
"eu": "baskišćina",
|
||||
"fa": "persišćina",
|
||||
"fi": "finšćina",
|
||||
"fr": "francošćina",
|
||||
"frCA": "francošćina (Kanada)",
|
||||
"he": "hebrejšćina",
|
||||
"hi": "hindišćina",
|
||||
"hr": "chorwatšćina",
|
||||
"hsb": "hornjoserbšćina",
|
||||
"hu": "madźaršćina",
|
||||
"hy": "armenšćina",
|
||||
"id": "indonešćina",
|
||||
"it": "italšćina",
|
||||
"ja": "japanšćina",
|
||||
"ko": "korejšćina",
|
||||
"lt": "litawšćina",
|
||||
"lv": "letišćina",
|
||||
"nl": "nižozemšćina",
|
||||
"pl": "pólšćina",
|
||||
"pt": "portugalšćina",
|
||||
"ptBR": "portugalšćina (Brazilska)",
|
||||
"ro": "rumunšćina",
|
||||
"ru": "rušćina",
|
||||
"sk": "słowakšćina",
|
||||
"sl": "słowjenšćina",
|
||||
"sq": "albanšćina",
|
||||
"sr": "serbišćina",
|
||||
"sv": "šwedšćina",
|
||||
"tr": "turkowšćina",
|
||||
"uk": "ukrainšćina",
|
||||
"vi": "vietnamšćina",
|
||||
"zhCN": "chinšćina (China)",
|
||||
"zhTW": "chinšćina (Taiwan)"
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
"he": "Hebrew",
|
||||
"hi": "Hindi",
|
||||
"hr": "Croatian",
|
||||
"hsb": "Upper Sorbian",
|
||||
"hu": "Hungarian",
|
||||
"hy": "Armenian",
|
||||
"id": "Indonesian",
|
||||
|
||||
@@ -894,12 +894,20 @@
|
||||
"limitNotificationDescriptionWeb": "نظرًا للضغط الكبير، سيقيَّد التسجيل إلى {{limit}} د، ولكن إن أردت التسجيل لمدة مفتوحة، جرِّب <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
|
||||
"linkGenerated": "لقد أنشأنا رابطًا لتسجيلك.",
|
||||
"live": "مباشر",
|
||||
"localRecordingNoNotificationWarning": "لن يتم الإعلان عن التسجيل للمشاركين الآخرين. ستحتاج إلى إخبارهم بأنه تم تسجيل الاجتماع.",
|
||||
"localRecordingNoVideo": "لا يتم تسجيل الفيديو",
|
||||
"localRecordingStartWarning": "يرجى التأكد من إيقاف التسجيل قبل الخروج من الاجتماع من أجل حفظه.",
|
||||
"localRecordingStartWarningTitle": "أوقف التسجيل لحفظه",
|
||||
"localRecordingVideoStop": "سيؤدي إيقاف الفيديو أيضًا إلى إيقاف التسجيل المحلي. هل أنت متأكد أنك تريد الاستمرار؟",
|
||||
"localRecordingVideoWarning": "لتسجيل الفيديو الخاص بك ، يجب أن يكون لديك عند بدء التسجيل",
|
||||
"localRecordingWarning": "تأكد من تحديد علامة التبويب الحالية لاستخدام الفيديو والصوت الصحيحين. التسجيل محدود حاليًا بـ 1 كيغابايت ، أي حوالي 100 دقيقة.",
|
||||
"loggedIn": "مُسجَّل باسم {{userName}}",
|
||||
"noStreams": "لم يتم الكشف عن دفق الصوت أو الفيديو.",
|
||||
"off": "أوقِف التسجيل",
|
||||
"offBy": "أوقَف {{name}} التسجيل",
|
||||
"on": "تسجيل",
|
||||
"onBy": "بدأ {{name}} التسجيل",
|
||||
"onlyRecordSelf": "تسجيل فقط دفق الصوت والفيديو الخاصة بي",
|
||||
"pending": "التحضير لتسجيل المُلتقى...",
|
||||
"rec": "تسجيل",
|
||||
"saveLocalRecording": "حفظ ملف التسجيل محليا",
|
||||
@@ -949,6 +957,7 @@
|
||||
"name": "الاسم",
|
||||
"noDevice": "لا يوجد",
|
||||
"participantJoined": "انضم مشارك",
|
||||
"participantKnocking": "دخل المشارك في الردهة",
|
||||
"participantLeft": "غادر المشارك",
|
||||
"playSounds": "تشغيل الصوت عند:",
|
||||
"reactions": "ردود فعل المُلتقى",
|
||||
@@ -1024,6 +1033,7 @@
|
||||
"termsView": {
|
||||
"header": "مصطلحات"
|
||||
},
|
||||
"toggleTopPanelLabel": "تبديل اللوحة العلوية",
|
||||
"toolbar": {
|
||||
"Settings": "الإعدادات",
|
||||
"accessibilityLabel": {
|
||||
|
||||
@@ -40,9 +40,6 @@
|
||||
"audioOnly": {
|
||||
"audioOnly": "Geringe Bandbreite"
|
||||
},
|
||||
"blankPage": {
|
||||
"meetingEnded": "Konferenz beendet."
|
||||
},
|
||||
"breakoutRooms": {
|
||||
"actions": {
|
||||
"add": "Breakout-Raum hinzufügen",
|
||||
@@ -80,6 +77,17 @@
|
||||
"refresh": "Kalender aktualisieren",
|
||||
"today": "Heute"
|
||||
},
|
||||
"carmode": {
|
||||
"actions": {
|
||||
"leaveMeeting": "Konferenz verlassen",
|
||||
"selectSoundDevice": "Audiogerät auswählen"
|
||||
},
|
||||
"labels": {
|
||||
"buttonLabel": "Automodus",
|
||||
"title": "Automodus",
|
||||
"videoStopped": "Ihre Kamera ist deaktiviert"
|
||||
}
|
||||
},
|
||||
"chat": {
|
||||
"enter": "Chat-Raum betreten",
|
||||
"error": "Fehler: Ihre Nachricht wurde nicht versendet. Grund: {{error}}",
|
||||
@@ -108,6 +116,7 @@
|
||||
},
|
||||
"chromeExtensionBanner": {
|
||||
"buttonText": "Chrome-Erweiterung installieren",
|
||||
"buttonTextEdge": "Edge-Erweiterung installieren",
|
||||
"close": "Schließen",
|
||||
"dontShowAgain": "Hinweis nicht mehr anzeigen",
|
||||
"installExtensionText": "Installieren Sie die Erweiterung für die Integration von Google Calendar und Office 365"
|
||||
@@ -199,6 +208,9 @@
|
||||
"selectADevice": "Ein Gerät wählen",
|
||||
"testAudio": "Prüfton wiedergeben"
|
||||
},
|
||||
"dialIn": {
|
||||
"screenTitle": "Einwahldaten"
|
||||
},
|
||||
"dialOut": {
|
||||
"statusMessage": "ist jetzt {{status}}"
|
||||
},
|
||||
@@ -649,6 +661,8 @@
|
||||
"linkToSalesforceKey": "Konferenz verlinken",
|
||||
"linkToSalesforceProgress": "Konferenz wird mit Salesforce verlinkt...",
|
||||
"linkToSalesforceSuccess": "Die Konferenz wurde mit Salesforce verlinkt",
|
||||
"localRecordingStarted": "{{name}} hat eine lokale Aufzeichnung gestartet.",
|
||||
"localRecordingStopped": "{{name}} hat eine lokale Aufzeichnung gestoppt.",
|
||||
"me": "Ich",
|
||||
"moderationInEffectCSDescription": "Bitte melden um ein Video zu teilen",
|
||||
"moderationInEffectCSTitle": "Die Videofreigabe ist von der Moderation gesperrt",
|
||||
@@ -804,6 +818,7 @@
|
||||
"initiated": "Anruf gestartet",
|
||||
"joinAudioByPhone": "Per Telefon teilnehmen",
|
||||
"joinMeeting": "Konferenz beitreten",
|
||||
"joinMeetingInLowBandwidthMode": "Konferenz im Datensparmodus beitreten",
|
||||
"joinWithoutAudio": "Ohne Ton beitreten",
|
||||
"keyboardShortcuts": "Tastaturkurzbefehle aktivieren",
|
||||
"linkCopied": "Link in die Zwischenablage kopiert",
|
||||
@@ -879,13 +894,23 @@
|
||||
"limitNotificationDescriptionWeb": "Wegen hoher Nachfrage ist Ihre Aufnahme auf {{limit}} Min. begrenzt. Für unlimitierte Aufnahmen nutzen Sie bitte <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
|
||||
"linkGenerated": "Link zur Aufzeichnung wurde generiert.",
|
||||
"live": "LIVE",
|
||||
"localRecordingNoNotificationWarning": "Die Aufzeichnung wird anderen Anwesenden nicht mitgeteilt. Sie müssen diese selbst darauf hinweisen, dass die Konferenz aufgezeichnet wird.",
|
||||
"localRecordingNoVideo": "Videos werden nicht aufgenommen",
|
||||
"localRecordingStartWarning": "Bitte beenden Sie die Aufzeichnung vor dem Verlassen der Konferenz, um die Aufzeichnung zu speichern.",
|
||||
"localRecordingStartWarningTitle": "Aufzeichnung zum Speichern beenden",
|
||||
"localRecordingVideoStop": "Wenn Sie ihre Kamera abschalten wird auch die Aufnahme beendet. Sind Sie sicher, dass Sie fortfahren möchten?",
|
||||
"localRecordingVideoWarning": "Um Ihr eigenes Kamerabild aufzuzeichnen, müssen Sie Ihre Kamera beim Start der Aufnahme einschalten",
|
||||
"localRecordingWarning": "Bitte prüfen Sie, dass das aktuelle Tab auswählen, um Bild und Ton aufzuzeichnen. Die Länge der Aufzeichnung ist aktuell auf 1GB beschränkt, was ungefähr 100 Minuten entspricht.",
|
||||
"loggedIn": "Als {{userName}} angemeldet",
|
||||
"noStreams": "Kein Ton oder Video erkannt.",
|
||||
"off": "Aufnahme gestoppt",
|
||||
"offBy": "{{name}} stoppte die Aufnahme",
|
||||
"on": "Aufnahme",
|
||||
"onBy": "{{name}} startete die Aufnahme",
|
||||
"onlyRecordSelf": "Nur eigenes Kamerabild und Ton aufzeichnen",
|
||||
"pending": "Aufzeichnung des Meetings wird vorbereitet…",
|
||||
"rec": "AUFZ",
|
||||
"saveLocalRecording": "Aufzeichnung lokal abspeichern",
|
||||
"serviceDescription": "Ihre Aufzeichnung wird vom Aufzeichnungsdienst gespeichert",
|
||||
"serviceDescriptionCloud": "Cloud-Aufzeichnung",
|
||||
"serviceDescriptionCloudInfo": "Aufzeichnungen werden 24 Stunden nach Aufzeichnungsende automatisch gelöscht.",
|
||||
@@ -893,10 +918,12 @@
|
||||
"sessionAlreadyActive": "Diese Konferenz wird bereits aufgezeichnet.",
|
||||
"signIn": "Anmelden",
|
||||
"signOut": "Abmelden",
|
||||
"surfaceError": "Bitte das aktuelle Tab auswählen.",
|
||||
"unavailable": "Oh! Der {{serviceName}} ist aktuell nicht verfügbar. Wir arbeiten an der Behebung des Problems. Bitte versuchen Sie es später noch einmal.",
|
||||
"unavailableTitle": "Aufnahme nicht verfügbar",
|
||||
"uploadToCloud": "In die Cloud hochladen"
|
||||
},
|
||||
"screenshareDisplayName": "{{name}}s Bildschirmfreigabe",
|
||||
"sectionList": {
|
||||
"pullToRefresh": "Ziehen, um zu aktualisieren"
|
||||
},
|
||||
@@ -930,10 +957,12 @@
|
||||
"name": "Name",
|
||||
"noDevice": "Kein",
|
||||
"participantJoined": "Neue Person nimmt teil",
|
||||
"participantKnocking": "Person hat Lobby betreten",
|
||||
"participantLeft": "Person verlässt die Konferenz",
|
||||
"playSounds": "Hinweistöne aktiviert",
|
||||
"reactions": "Interaktionen",
|
||||
"sameAsSystem": "Wie System ({{label}})",
|
||||
"screenTitle": "Einstellungen",
|
||||
"selectAudioOutput": "Audioausgabe",
|
||||
"selectCamera": "Kamera",
|
||||
"selectMic": "Mikrofon",
|
||||
@@ -959,6 +988,7 @@
|
||||
"disableCrashReportingWarning": "Möchten Sie die Absturzberichte wirklich deaktivieren? Diese Einstellung wird nach einem Neustart der App wirksam.",
|
||||
"disableP2P": "Ende-zu-Ende-Modus deaktivieren",
|
||||
"displayName": "Anzeigename",
|
||||
"displayNamePlaceholderText": "z.B. Erika Musterfrau",
|
||||
"email": "E-Mail",
|
||||
"header": "Einstellungen",
|
||||
"profileSection": "Profil",
|
||||
@@ -1003,6 +1033,7 @@
|
||||
"termsView": {
|
||||
"header": "Nutzungsbedingungen"
|
||||
},
|
||||
"toggleTopPanelLabel": "Obere Leiste ein-/ausschalten",
|
||||
"toolbar": {
|
||||
"Settings": "Einstellungen",
|
||||
"accessibilityLabel": {
|
||||
@@ -1012,10 +1043,12 @@
|
||||
"boo": "Buhen",
|
||||
"breakoutRoom": "Breakout-Räume betreten/verlassen",
|
||||
"callQuality": "Qualitätseinstellungen",
|
||||
"carmode": "Automodus",
|
||||
"cc": "Untertitel ein-/ausschalten",
|
||||
"chat": "Chatfenster öffnen / schließen",
|
||||
"clap": "Klatschen",
|
||||
"collapse": "Einklappen",
|
||||
"dock": "In Hauptfenster einbinden",
|
||||
"document": "Geteiltes Dokument schließen",
|
||||
"download": "Unsere Apps herunterladen",
|
||||
"embedMeeting": "Konferenz einbetten",
|
||||
@@ -1066,6 +1099,7 @@
|
||||
"tileView": "Kachelansicht ein-/ausschalten",
|
||||
"toggleCamera": "Kamera wechseln",
|
||||
"toggleFilmstrip": "Miniaturansichten ein-/ausschalten",
|
||||
"undock": "In eigenem Fenster anzeigen",
|
||||
"videoblur": "Unscharfer Hintergrund ein-/ausschalten",
|
||||
"videomute": "„Video stummschalten“ ein-/ausschalten"
|
||||
},
|
||||
@@ -1082,6 +1116,7 @@
|
||||
"closeChat": "Chat schließen",
|
||||
"closeReactionsMenu": "Interaktionsmenü schließen",
|
||||
"disableReactionSounds": "Sie können die Interaktionstöne für diese Konferenz deaktivieren",
|
||||
"dock": "In Hauptfenster einbinden",
|
||||
"documentClose": "Geteiltes Dokument schließen",
|
||||
"documentOpen": "Geteiltes Dokument öffnen",
|
||||
"download": "Unsere Apps herunterladen",
|
||||
@@ -1150,6 +1185,7 @@
|
||||
"talkWhileMutedPopup": "Versuchen Sie zu sprechen? Ihr Mikrofon ist stummgeschaltet.",
|
||||
"tileViewToggle": "Kachelansicht ein-/ausschalten",
|
||||
"toggleCamera": "Kamera wechseln",
|
||||
"undock": "In eigenem Fenster anzeigen",
|
||||
"videoSettings": "Kameraeinstellungen",
|
||||
"videomute": "Kamera starten / stoppen"
|
||||
},
|
||||
|
||||
1324
lang/main-hsb.json
Normal file
1324
lang/main-hsb.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -894,15 +894,23 @@
|
||||
"limitNotificationDescriptionWeb": "Devido à grande procura, a sua gravação será limitada a {{limit}} min. For unlimited recordings try <a href={{url}} rel='noopener noreferrer' target='_blank'>{{app}}</a>.",
|
||||
"linkGenerated": "Gerámos um link para a sua gravação.",
|
||||
"live": "DIRETO",
|
||||
"localRecordingWarning": "Certifique-se de seleccionar o separador actual a fim de utilizar o vídeo e áudio correctos. A gravação está actualmente limitada a 1 GB, o que é cerca de 100 minutos.",
|
||||
"localRecordingNoNotificationWarning": "A gravação não será anunciada aos outros participantes. Será necessário avisá-los de que a reunião está gravada.",
|
||||
"localRecordingNoVideo": "O vídeo não está a ser gravado",
|
||||
"localRecordingStartWarning": "Por favor, certifique-se de que pára a gravação antes de sair da reunião a fim de a guardar.",
|
||||
"localRecordingStartWarningTitle": "Parar a gravação para a salvar",
|
||||
"localRecordingVideoStop": "A paragem do seu vídeo também irá parar a gravação local. Tem a certeza de que quer continuar?",
|
||||
"localRecordingVideoWarning": "Para gravar o seu vídeo deve tê-lo ligado quando iniciar a gravação",
|
||||
"localRecordingWarning": "Certifique-se de selecionar o separador actual a fim de utilizar o vídeo e áudio corretos. A gravação está actualmente limitada a 1 GB, o que é cerca de 100 minutos.",
|
||||
"loggedIn": "Conectado como {{userName}}",
|
||||
"noStreams": "Não foi detetado nenhum sinal áudio ou vídeo.",
|
||||
"off": "Gravação parada",
|
||||
"offBy": "{{name}} parou a gravação",
|
||||
"on": "Gravando",
|
||||
"onBy": "{{name}} iniciou a gravação",
|
||||
"onlyRecordSelf": "Gravar apenas as minhas transmissões áudio e vídeo",
|
||||
"pending": "Preparando para gravar a reunião...",
|
||||
"rec": "REC",
|
||||
"saveLocalRecording": "Guardar ficheiro de gravação localmente",
|
||||
"saveLocalRecording": "Guardar ficheiro de gravação localmente (Beta)",
|
||||
"serviceDescription": "Sua gravação será salva pelo serviço de gravação",
|
||||
"serviceDescriptionCloud": "Gravação na nuvem",
|
||||
"serviceDescriptionCloudInfo": "As reuniões gravadas são automaticamente apagadas 24h após a hora de gravação.",
|
||||
@@ -915,7 +923,7 @@
|
||||
"unavailableTitle": "Gravação indisponível",
|
||||
"uploadToCloud": "Enviar para a nuvem"
|
||||
},
|
||||
"screenshareDisplayName": "Ecrã de {{name}}",
|
||||
"screenshareDisplayName": "Ecrã do/a {{name}}",
|
||||
"sectionList": {
|
||||
"pullToRefresh": "Puxe para atualizar"
|
||||
},
|
||||
@@ -949,6 +957,7 @@
|
||||
"name": "Nome",
|
||||
"noDevice": "Nenhum",
|
||||
"participantJoined": "Entrar um participante",
|
||||
"participantKnocking": "Participante entrou na sala de espera",
|
||||
"participantLeft": "Sair um participante",
|
||||
"playSounds": "Reproduzir som quando",
|
||||
"reactions": "Expressarem uma reação",
|
||||
@@ -1024,6 +1033,7 @@
|
||||
"termsView": {
|
||||
"header": "Termos"
|
||||
},
|
||||
"toggleTopPanelLabel": "Mostrar ou ocultar o painel superior",
|
||||
"toolbar": {
|
||||
"Settings": "Definições",
|
||||
"accessibilityLabel": {
|
||||
@@ -1038,7 +1048,7 @@
|
||||
"chat": "Abrir / Fechar chat",
|
||||
"clap": "Aplausos",
|
||||
"collapse": "Colapsar",
|
||||
"dock": "Encaixar na janela principal",
|
||||
"dock": "Ancorar na janela principal",
|
||||
"document": "Mudar para documento partilhado",
|
||||
"download": "Descarregar as nossas aplicações",
|
||||
"embedMeeting": "Reunião incorporada",
|
||||
@@ -1089,7 +1099,7 @@
|
||||
"tileView": "Mudar a vista em quadrícula",
|
||||
"toggleCamera": "Mudar a câmara",
|
||||
"toggleFilmstrip": "Mudar a película de filme",
|
||||
"undock": "Soltar numa janela separada",
|
||||
"undock": "Desancorar numa janela separada",
|
||||
"videoblur": "Mudar o desfoque de vídeo",
|
||||
"videomute": "Iniciar / Parar câmara"
|
||||
},
|
||||
@@ -1106,7 +1116,7 @@
|
||||
"closeChat": "Fechar chat",
|
||||
"closeReactionsMenu": "Fechar menu de reações",
|
||||
"disableReactionSounds": "Pode desactivar os sons de reacção para esta reunião",
|
||||
"dock": "Encaixar na janela principal",
|
||||
"dock": "Ancorar na janela principal",
|
||||
"documentClose": "Fechar documento partilhado",
|
||||
"documentOpen": "Abrir documento partilhado",
|
||||
"download": "Descarregar as nossas aplicações",
|
||||
@@ -1175,7 +1185,7 @@
|
||||
"talkWhileMutedPopup": "Está a tentar falar? Está com o microfone desativado.",
|
||||
"tileViewToggle": "Mudar para vista em quadrícula",
|
||||
"toggleCamera": "Mudar a câmara",
|
||||
"undock": "Soltar numa janela separada",
|
||||
"undock": "Desancorar numa janela separada",
|
||||
"videoSettings": "Definições de vídeo",
|
||||
"videomute": "Iniciar / Parar câmara"
|
||||
},
|
||||
|
||||
@@ -652,7 +652,7 @@
|
||||
"moderationInEffectTitle": "Ваш микрофон отключен модератором",
|
||||
"moderationInEffectVideoDescription": "Пожалуйста, поднимите руку, если хотите включить камеру.",
|
||||
"moderationInEffectVideoTitle": "Ваша камера заблокирована модератором",
|
||||
"moderationRequestFromModerator": "Хозяин хочет, чтобы вы включили звук",
|
||||
"moderationRequestFromModerator": "Модератор хочет, чтобы вы включили звук",
|
||||
"moderationRequestFromParticipant": "Хочет говорить",
|
||||
"moderationStartedTitle": "Началась модерация",
|
||||
"moderationStoppedTitle": "Модерация остановлена",
|
||||
|
||||
@@ -895,15 +895,22 @@
|
||||
"linkGenerated": "We have generated a link to your recording.",
|
||||
"live": "LIVE",
|
||||
"localRecordingNoNotificationWarning": "The recording will not be announced to other participants. You will need to let them know that the meeting is recorded.",
|
||||
"localRecordingNoVideo": "Video is not being recorded",
|
||||
"localRecordingStartWarning": "Please make sure you stop the recording before exiting the meeting in order to save it.",
|
||||
"localRecordingStartWarningTitle": "Stop the recording to save it",
|
||||
"localRecordingVideoStop": "Stopping your video will also stop the local recording. Are you sure you want to continue?",
|
||||
"localRecordingVideoWarning": "To record your video you must have it on when starting the recording",
|
||||
"localRecordingWarning": "Make sure you select the current tab in order to use the right video and audio. The recording is currently limited to 1GB, which is around 100 minutes.",
|
||||
"loggedIn": "Logged in as {{userName}}",
|
||||
"noStreams": "No audio or video stream detected.",
|
||||
"off": "Recording stopped",
|
||||
"offBy": "{{name}} stopped the recording",
|
||||
"on": "Recording started",
|
||||
"onBy": "{{name}} started the recording",
|
||||
"onlyRecordSelf": "Record only my audio and video streams",
|
||||
"pending": "Preparing to record the meeting...",
|
||||
"rec": "REC",
|
||||
"saveLocalRecording": "Save recording file locally",
|
||||
"saveLocalRecording": "Save recording file locally (Beta)",
|
||||
"serviceDescription": "Your recording will be saved by the recording service",
|
||||
"serviceDescriptionCloud": "Cloud recording",
|
||||
"serviceDescriptionCloudInfo": "Recorded meetings are automatically cleared 24h after their recording time.",
|
||||
@@ -950,6 +957,7 @@
|
||||
"name": "Name",
|
||||
"noDevice": "None",
|
||||
"participantJoined": "Participant Joined",
|
||||
"participantKnocking": "Participant entered lobby",
|
||||
"participantLeft": "Participant Left",
|
||||
"playSounds": "Play sound on",
|
||||
"reactions": "Meeting reactions",
|
||||
@@ -1025,6 +1033,7 @@
|
||||
"termsView": {
|
||||
"header": "Terms"
|
||||
},
|
||||
"toggleTopPanelLabel": "Toggle top panel",
|
||||
"toolbar": {
|
||||
"Settings": "Settings",
|
||||
"accessibilityLabel": {
|
||||
|
||||
@@ -474,11 +474,11 @@ function initCommands() {
|
||||
* @param { string } arg.mode - Recording mode, either `file` or `stream`.
|
||||
* @param { string } arg.dropboxToken - Dropbox oauth2 token.
|
||||
* @param { string } arg.rtmpStreamKey - The RTMP stream key.
|
||||
* @param { string } arg.rtmpBroadcastID - The RTMP braodcast ID.
|
||||
* @param { string } arg.rtmpBroadcastID - The RTMP broadcast ID.
|
||||
* @param { boolean } arg.shouldShare - Whether the recording should be shared with the participants or not.
|
||||
* Only applies to certain jitsi meet deploys.
|
||||
* @param { string } arg.youtubeStreamKey - The youtube stream key.
|
||||
* @param { string } arg.youtubeBroadcastID - The youtube broacast ID.
|
||||
* @param { string } arg.youtubeBroadcastID - The youtube broadcast ID.
|
||||
* @returns {void}
|
||||
*/
|
||||
'start-recording': ({
|
||||
@@ -1642,7 +1642,7 @@ class API {
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) that an error occured.
|
||||
* Notify external application (if API is enabled) that an error occurred.
|
||||
*
|
||||
* @param {Object} error - The error.
|
||||
* @returns {void}
|
||||
@@ -1670,7 +1670,7 @@ class API {
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify external application (if API is enabled) wether the used browser is supported or not.
|
||||
* Notify external application (if API is enabled) whether the used browser is supported or not.
|
||||
*
|
||||
* @param {boolean} supported - If browser is supported or not.
|
||||
* @returns {void}
|
||||
@@ -1698,7 +1698,7 @@ class API {
|
||||
/**
|
||||
* Notify the external application that the state of the participants pane changed.
|
||||
*
|
||||
* @param {boolean} open - Wether the panel is open or not.
|
||||
* @param {boolean} open - Whether the panel is open or not.
|
||||
* @returns {void}
|
||||
*/
|
||||
notifyParticipantsPaneToggled(open) {
|
||||
|
||||
6
modules/API/external/external_api.js
vendored
6
modules/API/external/external_api.js
vendored
@@ -1000,7 +1000,7 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns wether meeting is started silent.
|
||||
* Returns whether meeting is started silent.
|
||||
*
|
||||
* @returns {Promise} - Resolves with start silent status.
|
||||
*/
|
||||
@@ -1242,9 +1242,9 @@ export default class JitsiMeetExternalAPI extends EventEmitter {
|
||||
* @param { boolean } options.shouldShare - Whether the recording should be shared with the participants or not.
|
||||
* Only applies to certain jitsi meet deploys.
|
||||
* @param { string } options.rtmpStreamKey - The RTMP stream key.
|
||||
* @param { string } options.rtmpBroadcastID - The RTMP broacast ID.
|
||||
* @param { string } options.rtmpBroadcastID - The RTMP broadcast ID.
|
||||
* @param { string } options.youtubeStreamKey - The youtube stream key.
|
||||
* @param { string } options.youtubeBroadcastID - The youtube broacast ID.
|
||||
* @param { string } options.youtubeBroadcastID - The youtube broadcast ID.
|
||||
* @returns {void}
|
||||
*/
|
||||
startRecording(options) {
|
||||
|
||||
@@ -17,7 +17,6 @@ import {
|
||||
import { getReplaceParticipant } from '../../../react/features/base/config/functions';
|
||||
import { isDialogOpen } from '../../../react/features/base/dialog';
|
||||
import { setJWT } from '../../../react/features/base/jwt';
|
||||
import { setPrejoinPageVisibility } from '../../../react/features/prejoin';
|
||||
import UIUtil from '../util/UIUtil';
|
||||
|
||||
import ExternalLoginDialog from './LoginDialog';
|
||||
@@ -181,7 +180,6 @@ function authenticate(room: Object, lockPassword: string) {
|
||||
if (isTokenAuthEnabled(config) || room.isExternalAuthEnabled()) {
|
||||
doExternalAuth(room, lockPassword);
|
||||
} else {
|
||||
APP.store.dispatch(setPrejoinPageVisibility(false));
|
||||
APP.store.dispatch(openLoginDialog());
|
||||
}
|
||||
}
|
||||
|
||||
875
package-lock.json
generated
875
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
17
package.json
17
package.json
@@ -54,7 +54,7 @@
|
||||
"@react-navigation/elements": "1.2.1",
|
||||
"@react-navigation/material-top-tabs": "6.0.6",
|
||||
"@react-navigation/native": "6.0.6",
|
||||
"@react-navigation/native-stack": "6.6.2",
|
||||
"@react-navigation/stack": "6.2.2",
|
||||
"@svgr/webpack": "4.3.2",
|
||||
"@tensorflow/tfjs-backend-wasm": "3.13.0",
|
||||
"@tensorflow/tfjs-core": "3.13.0",
|
||||
@@ -79,9 +79,9 @@
|
||||
"js-md5": "0.6.1",
|
||||
"js-sha512": "0.8.0",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1457.0.0+ad75454f/lib-jitsi-meet.tgz",
|
||||
"lib-jitsi-meet": "https://github.com/jitsi/lib-jitsi-meet/releases/download/v1468.0.0+634885b9/lib-jitsi-meet.tgz",
|
||||
"lodash": "4.17.21",
|
||||
"moment": "2.29.2",
|
||||
"moment": "2.29.4",
|
||||
"moment-duration-format": "2.2.2",
|
||||
"optional-require": "1.0.3",
|
||||
"promise.allsettled": "1.0.4",
|
||||
@@ -109,7 +109,7 @@
|
||||
"react-native-performance": "2.1.0",
|
||||
"react-native-reanimated": "https://git@github.com/software-mansion/react-native-reanimated#c4a6b6f687ede090f6081064abe83a2ef9a05784",
|
||||
"react-native-safe-area-context": "3.3.2",
|
||||
"react-native-screens": "3.10.1",
|
||||
"react-native-screens": "3.13.1",
|
||||
"react-native-sound": "0.11.1",
|
||||
"react-native-splash-screen": "3.3.0",
|
||||
"react-native-svg": "12.1.0",
|
||||
@@ -130,6 +130,7 @@
|
||||
"redux-thunk": "2.2.0",
|
||||
"resemblejs": "4.0.0",
|
||||
"rnnoise-wasm": "https://git@github.com/jitsi/rnnoise-wasm#566a16885897704d6e6d67a1d5ac5d39781db2af",
|
||||
"seamless-scroll-polyfill": "2.1.8",
|
||||
"styled-components": "3.4.9",
|
||||
"util": "0.12.1",
|
||||
"uuid": "8.3.2",
|
||||
@@ -147,10 +148,14 @@
|
||||
"@babel/preset-react": "7.16.0",
|
||||
"@babel/runtime": "7.16.0",
|
||||
"@jitsi/eslint-config": "4.0.0",
|
||||
"@types/js-md5": "0.4.3",
|
||||
"@types/lodash": "4.14.182",
|
||||
"@types/react": "17.0.14",
|
||||
"@types/react-native": "0.67.6",
|
||||
"@types/react-redux": "7.1.24",
|
||||
"@types/uuid": "8.3.4",
|
||||
"@typescript-eslint/eslint-plugin": "5.30.5",
|
||||
"@typescript-eslint/parser": "5.30.4",
|
||||
"babel-loader": "8.2.3",
|
||||
"babel-plugin-optional-require": "0.3.1",
|
||||
"circular-dependency-plugin": "5.2.0",
|
||||
@@ -186,9 +191,9 @@
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"lint": "eslint --max-warnings 0 .",
|
||||
"lint": "eslint --ext .js,.ts,.tsx --max-warnings 0 . && tsc --noEmit --skipLibCheck",
|
||||
"lang-sort": "./resources/lang-sort.sh",
|
||||
"lint-fix": "eslint --max-warnings 0 --fix .",
|
||||
"lint-fix": "eslint --ext .js,.ts,.tsx --max-warnings 0 --fix .",
|
||||
"postinstall": "patch-package --error-on-fail && jetify",
|
||||
"validate": "npm ls",
|
||||
"start": "make dev"
|
||||
|
||||
@@ -6,6 +6,27 @@ module.exports = {
|
||||
'@jitsi/eslint-config/react',
|
||||
'.eslintrc-react-native.js'
|
||||
],
|
||||
'overrides': [
|
||||
{
|
||||
'files': [ '*.ts', '*.tsx' ],
|
||||
parser: '@typescript-eslint/parser',
|
||||
rules: {
|
||||
'no-undef': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/ban-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'no-prototype-builtins': 'off'
|
||||
},
|
||||
'plugins': [ '@typescript-eslint' ],
|
||||
'extends': [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended'
|
||||
]
|
||||
}
|
||||
],
|
||||
'rules': {
|
||||
'flowtype/no-types-missing-file-annotation': 0,
|
||||
|
||||
|
||||
@@ -690,7 +690,7 @@ export function createStartSilentEvent() {
|
||||
/**
|
||||
* Creates an event which indicates that HTMLAudioElement.play has failed.
|
||||
*
|
||||
* @param {sting} elementID - The ID of the HTMLAudioElement.
|
||||
* @param {string} elementID - The ID of the HTMLAudioElement.
|
||||
* @returns {Object} The event in a format suitable for sending via sendAnalytics.
|
||||
*/
|
||||
export function createAudioPlayErrorEvent(elementID) {
|
||||
@@ -703,9 +703,9 @@ export function createAudioPlayErrorEvent(elementID) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an event which indicates that HTMLAudioElement.play has succeded after a prior failure.
|
||||
* Creates an event which indicates that HTMLAudioElement.play has succeeded after a prior failure.
|
||||
*
|
||||
* @param {sting} elementID - The ID of the HTMLAudioElement.
|
||||
* @param {string} elementID - The ID of the HTMLAudioElement.
|
||||
* @returns {Object} The event in a format suitable for sending via sendAnalytics.
|
||||
*/
|
||||
export function createAudioPlaySuccessEvent(elementID) {
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
// @flow
|
||||
|
||||
import { ReducerRegistry } from '../base/redux';
|
||||
import ReducerRegistry from '../base/redux/ReducerRegistry';
|
||||
|
||||
import { UPDATE_LOCAL_TRACKS_DURATION } from './actionTypes';
|
||||
|
||||
@@ -30,6 +28,22 @@ const DEFAULT_STATE = {
|
||||
}
|
||||
};
|
||||
|
||||
interface Value {
|
||||
startedTime: number,
|
||||
value: number
|
||||
}
|
||||
|
||||
export interface IAnalyticsState {
|
||||
localTracksDuration: {
|
||||
audio: Value,
|
||||
video: {
|
||||
camera: Value,
|
||||
desktop: Value
|
||||
},
|
||||
conference: Value
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for actions which changes the state of the analytics feature.
|
||||
*
|
||||
@@ -38,7 +52,7 @@ const DEFAULT_STATE = {
|
||||
* @param {string} action.type - Type of action.
|
||||
* @returns {Object}
|
||||
*/
|
||||
ReducerRegistry.register('features/analytics', (state = DEFAULT_STATE, action) => {
|
||||
ReducerRegistry.register('features/analytics', (state: IAnalyticsState = DEFAULT_STATE, action: any) => {
|
||||
switch (action.type) {
|
||||
case UPDATE_LOCAL_TRACKS_DURATION:
|
||||
return {
|
||||
@@ -44,7 +44,7 @@ export * from './actions.any';
|
||||
* scheme, or a mere room name.
|
||||
* @returns {Function}
|
||||
*/
|
||||
export function appNavigate(uri: ?string) {
|
||||
export function appNavigate(uri?: string) {
|
||||
logger.info(`appNavigate to ${uri}`);
|
||||
|
||||
return async (dispatch: Dispatch<any>, getState: Function) => {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import { Platform, StyleSheet, View } from 'react-native';
|
||||
import { SafeAreaProvider } from 'react-native-safe-area-context';
|
||||
import { FullWindowOverlay } from 'react-native-screens';
|
||||
import SplashScreen from 'react-native-splash-screen';
|
||||
|
||||
import { DialogContainer } from '../../base/dialog';
|
||||
@@ -24,7 +23,6 @@ import '../reducers';
|
||||
declare var __DEV__;
|
||||
|
||||
const DialogContainerWrapper = Platform.select({
|
||||
ios: FullWindowOverlay,
|
||||
default: View
|
||||
});
|
||||
|
||||
@@ -33,11 +31,6 @@ const DialogContainerWrapper = Platform.select({
|
||||
*/
|
||||
type Props = AbstractAppProps & {
|
||||
|
||||
/**
|
||||
* Identifier for this app on the native side.
|
||||
*/
|
||||
externalAPIScope: string,
|
||||
|
||||
/**
|
||||
* An object with the feature flags.
|
||||
*/
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import '../authentication/middleware';
|
||||
import '../base/i18n/middleware';
|
||||
import '../base/devices/middleware';
|
||||
import '../base/media/middleware';
|
||||
import '../dynamic-branding/middleware';
|
||||
import '../e2ee/middleware';
|
||||
import '../external-api/middleware';
|
||||
|
||||
@@ -1,4 +1,28 @@
|
||||
import { IAnalyticsState } from '../analytics/reducer';
|
||||
import { IAuthenticationState } from '../authentication/reducer';
|
||||
import { IAVModerationState } from '../av-moderation/reducer';
|
||||
import { IAppState } from '../base/app/reducer';
|
||||
import { IAudioOnlyState } from '../base/audio-only/reducer';
|
||||
import { IConferenceState } from '../base/conference/reducer';
|
||||
import { IConfig } from '../base/config/configType';
|
||||
import { IConnectionState } from '../base/connection/reducer';
|
||||
import { IDevicesState } from '../base/devices/reducer';
|
||||
import { IDialogState } from '../base/dialog/reducer';
|
||||
|
||||
export interface IStore {
|
||||
getState: Function,
|
||||
dispatch: Function
|
||||
}
|
||||
|
||||
export interface IState {
|
||||
'features/analytics': IAnalyticsState,
|
||||
'features/authentication': IAuthenticationState,
|
||||
'features/av-moderation': IAVModerationState,
|
||||
'features/base/app': IAppState,
|
||||
'features/base/audio-only': IAudioOnlyState,
|
||||
'features/base/conference': IConferenceState,
|
||||
'features/base/config': IConfig,
|
||||
'features/base/connection': IConnectionState,
|
||||
'features/base/devices': IDevicesState,
|
||||
'features/base/dialog': IDialogState
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
JitsiConnectionErrors
|
||||
} from '../base/lib-jitsi-meet';
|
||||
import { MiddlewareRegistry } from '../base/redux';
|
||||
import { setPrejoinPageVisibility } from '../prejoin';
|
||||
|
||||
import {
|
||||
CANCEL_LOGIN,
|
||||
@@ -121,7 +120,6 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
&& error.name === JitsiConnectionErrors.PASSWORD_REQUIRED
|
||||
&& typeof error.recoverable === 'undefined') {
|
||||
error.recoverable = true;
|
||||
store.dispatch(setPrejoinPageVisibility(false));
|
||||
store.dispatch(openLoginDialog());
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// @flow
|
||||
|
||||
import { assign, ReducerRegistry } from '../base/redux';
|
||||
import ReducerRegistry from '../base/redux/ReducerRegistry';
|
||||
import { assign } from '../base/redux/functions';
|
||||
|
||||
import {
|
||||
CANCEL_LOGIN,
|
||||
@@ -10,6 +9,13 @@ import {
|
||||
WAIT_FOR_OWNER
|
||||
} from './actionTypes';
|
||||
|
||||
export interface IAuthenticationState {
|
||||
error?: Object|undefined;
|
||||
progress?: number|undefined;
|
||||
thenableWithCancel?: Object|undefined;
|
||||
waitForOwnerTimeoutID?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listens for actions which change the state of the authentication feature.
|
||||
*
|
||||
@@ -18,7 +24,7 @@ import {
|
||||
* @param {string} action.type - Type of action.
|
||||
* @returns {Object}
|
||||
*/
|
||||
ReducerRegistry.register('features/authentication', (state = {}, action) => {
|
||||
ReducerRegistry.register('features/authentication', (state: IAuthenticationState = {}, action: any) => {
|
||||
switch (action.type) {
|
||||
case CANCEL_LOGIN:
|
||||
return assign(state, {
|
||||
@@ -1,11 +1,9 @@
|
||||
// @flow
|
||||
|
||||
import { MEDIA_TYPE, type MediaType } from '../base/media/constants';
|
||||
import { MEDIA_TYPE } from '../base/media/constants';
|
||||
|
||||
/**
|
||||
* Mapping between a media type and the witelist reducer key.
|
||||
*/
|
||||
export const MEDIA_TYPE_TO_WHITELIST_STORE_KEY: {[key: MediaType]: string} = {
|
||||
export const MEDIA_TYPE_TO_WHITELIST_STORE_KEY: { [key: string]: string } = {
|
||||
[MEDIA_TYPE.AUDIO]: 'audioWhitelist',
|
||||
[MEDIA_TYPE.VIDEO]: 'videoWhitelist'
|
||||
};
|
||||
@@ -13,7 +11,7 @@ export const MEDIA_TYPE_TO_WHITELIST_STORE_KEY: {[key: MediaType]: string} = {
|
||||
/**
|
||||
* Mapping between a media type and the pending reducer key.
|
||||
*/
|
||||
export const MEDIA_TYPE_TO_PENDING_STORE_KEY: {[key: MediaType]: string} = {
|
||||
export const MEDIA_TYPE_TO_PENDING_STORE_KEY: {[key: string]: string} = {
|
||||
[MEDIA_TYPE.AUDIO]: 'pendingAudio',
|
||||
[MEDIA_TYPE.VIDEO]: 'pendingVideo'
|
||||
};
|
||||
@@ -107,7 +107,7 @@ export const isParticipantPending = (participant: Object, mediaType: MediaType)
|
||||
|
||||
/**
|
||||
* Selector which returns a list with all the participants asking to audio unmute.
|
||||
* This is visible ony for the moderator.
|
||||
* This is visible only for the moderator.
|
||||
*
|
||||
* @param {Object} state - The global state.
|
||||
* @returns {Array<Object>}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
/* @flow */
|
||||
|
||||
import { MEDIA_TYPE } from '../base/media/constants';
|
||||
import type { MediaType } from '../base/media/constants';
|
||||
import {
|
||||
PARTICIPANT_LEFT,
|
||||
PARTICIPANT_UPDATED
|
||||
|
||||
// @ts-ignore
|
||||
} from '../base/participants';
|
||||
import { ReducerRegistry } from '../base/redux';
|
||||
import ReducerRegistry from '../base/redux/ReducerRegistry';
|
||||
|
||||
import {
|
||||
DISABLE_MODERATION,
|
||||
@@ -29,8 +29,19 @@ const initialState = {
|
||||
pendingVideo: []
|
||||
};
|
||||
|
||||
export interface IAVModerationState {
|
||||
audioModerationEnabled: boolean;
|
||||
videoModerationEnabled: boolean;
|
||||
audioWhitelist: { [id: string]: boolean };
|
||||
videoWhitelist: { [id: string]: boolean };
|
||||
pendingAudio: Array<{ id: string }>;
|
||||
pendingVideo: Array<{ id: string }>;
|
||||
audioUnmuteApproved?: boolean|undefined;
|
||||
videoUnmuteApproved?: boolean|undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
Updates a participant in the state for the specified media type.
|
||||
* Updates a participant in the state for the specified media type.
|
||||
*
|
||||
* @param {MediaType} mediaType - The media type.
|
||||
* @param {Object} participant - Information about participant to be modified.
|
||||
@@ -38,11 +49,11 @@ const initialState = {
|
||||
* @private
|
||||
* @returns {boolean} - Whether state instance was modified.
|
||||
*/
|
||||
function _updatePendingParticipant(mediaType: MediaType, participant, state: Object = {}) {
|
||||
function _updatePendingParticipant(mediaType: MediaType, participant: any, state: any = {}) {
|
||||
let arrayItemChanged = false;
|
||||
const storeKey = MEDIA_TYPE_TO_PENDING_STORE_KEY[mediaType];
|
||||
const arr = state[storeKey];
|
||||
const newArr = arr.map(pending => {
|
||||
const newArr = arr.map((pending: { id: string}) => {
|
||||
if (pending.id === participant.id) {
|
||||
arrayItemChanged = true;
|
||||
|
||||
@@ -64,7 +75,7 @@ function _updatePendingParticipant(mediaType: MediaType, participant, state: Obj
|
||||
return false;
|
||||
}
|
||||
|
||||
ReducerRegistry.register('features/av-moderation', (state = initialState, action) => {
|
||||
ReducerRegistry.register('features/av-moderation', (state: IAVModerationState = initialState, action: any) => {
|
||||
|
||||
switch (action.type) {
|
||||
case DISABLE_MODERATION: {
|
||||
@@ -1,10 +1,12 @@
|
||||
// @flow
|
||||
|
||||
import { ReducerRegistry } from '../redux';
|
||||
import ReducerRegistry from '../redux/ReducerRegistry';
|
||||
|
||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from './actionTypes';
|
||||
|
||||
ReducerRegistry.register('features/base/app', (state = {}, action) => {
|
||||
export interface IAppState {
|
||||
app?: Object|undefined;
|
||||
}
|
||||
|
||||
ReducerRegistry.register('features/base/app', (state: IAppState = {}, action) => {
|
||||
switch (action.type) {
|
||||
case APP_WILL_MOUNT: {
|
||||
const { app } = action;
|
||||
@@ -1,16 +1,17 @@
|
||||
// @flow
|
||||
|
||||
import { ReducerRegistry } from '../redux';
|
||||
import ReducerRegistry from '../redux/ReducerRegistry';
|
||||
|
||||
import { SET_AUDIO_ONLY } from './actionTypes';
|
||||
|
||||
export interface IAudioOnlyState {
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
enabled: false
|
||||
};
|
||||
|
||||
|
||||
ReducerRegistry.register('features/base/audio-only', (state = DEFAULT_STATE, action) => {
|
||||
ReducerRegistry.register('features/base/audio-only', (state: IAudioOnlyState = DEFAULT_STATE, action) => {
|
||||
switch (action.type) {
|
||||
case SET_AUDIO_ONLY:
|
||||
return {
|
||||
@@ -21,7 +21,7 @@ const splitter = new GraphemeSplitter();
|
||||
* Generates the background color of an initials based avatar.
|
||||
*
|
||||
* @param {string?} initials - The initials of the avatar.
|
||||
* @param {Array<strig>} customAvatarBackgrounds - Custom avatar background values.
|
||||
* @param {Array<string>} customAvatarBackgrounds - Custom avatar background values.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getAvatarColor(initials: ?string, customAvatarBackgrounds: Array<string>) {
|
||||
|
||||
@@ -92,7 +92,7 @@ const useStyles = makeStyles(theme => {
|
||||
minHeight: '40px',
|
||||
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.action02Active,
|
||||
backgroundColor: theme.palette.ui02,
|
||||
|
||||
'& .indicators': {
|
||||
display: 'none'
|
||||
@@ -100,8 +100,8 @@ const useStyles = makeStyles(theme => {
|
||||
|
||||
'& .actions': {
|
||||
display: 'flex',
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.action02Active}`,
|
||||
backgroundColor: theme.palette.action02Active
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.ui02}`,
|
||||
backgroundColor: theme.palette.ui02
|
||||
}
|
||||
},
|
||||
|
||||
@@ -113,7 +113,7 @@ const useStyles = makeStyles(theme => {
|
||||
},
|
||||
|
||||
highlighted: {
|
||||
backgroundColor: theme.palette.action02Active
|
||||
backgroundColor: theme.palette.ui02
|
||||
},
|
||||
|
||||
detailsContainer: {
|
||||
@@ -159,8 +159,8 @@ const useStyles = makeStyles(theme => {
|
||||
|
||||
actionsContainer: {
|
||||
display: 'none',
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.action02Active}`,
|
||||
backgroundColor: theme.palette.action02Active
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.ui02}`,
|
||||
backgroundColor: theme.palette.ui02
|
||||
},
|
||||
|
||||
actionsPermanent: {
|
||||
@@ -171,8 +171,8 @@ const useStyles = makeStyles(theme => {
|
||||
|
||||
actionsVisible: {
|
||||
display: 'flex',
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.action02Active}`,
|
||||
backgroundColor: theme.palette.action02Active
|
||||
boxShadow: `-15px 0px 10px -5px ${theme.palette.ui02}`,
|
||||
backgroundColor: theme.palette.ui02
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
@@ -46,10 +46,7 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
case CONFERENCE_FAILED: {
|
||||
const errorName = action.error?.name;
|
||||
|
||||
if (errorName === JitsiConferenceErrors.MEMBERS_ONLY_ERROR
|
||||
|| errorName === JitsiConferenceErrors.PASSWORD_REQUIRED) {
|
||||
dispatch(setPrejoinPageVisibility(false));
|
||||
} else if (enableForcedReload && errorName === JitsiConferenceErrors.CONFERENCE_RESTARTED) {
|
||||
if (enableForcedReload && errorName === JitsiConferenceErrors.CONFERENCE_RESTARTED) {
|
||||
dispatch(setSkipPrejoinOnReload(true));
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
// @flow
|
||||
|
||||
/* eslint-disable import/order */
|
||||
// @ts-ignore
|
||||
import { LOCKED_LOCALLY, LOCKED_REMOTELY } from '../../room-lock';
|
||||
|
||||
// @ts-ignore
|
||||
import { CONNECTION_WILL_CONNECT, SET_LOCATION_URL } from '../connection';
|
||||
|
||||
// @ts-ignore
|
||||
import { JitsiConferenceErrors } from '../lib-jitsi-meet';
|
||||
import { assign, ReducerRegistry, set } from '../redux';
|
||||
|
||||
import { assign, set } from '../redux/functions';
|
||||
import ReducerRegistry from '../redux/ReducerRegistry';
|
||||
|
||||
import {
|
||||
AUTH_STATUS_CHANGED,
|
||||
@@ -25,6 +31,8 @@ import {
|
||||
SET_START_MUTED_POLICY,
|
||||
SET_START_REACTIONS_MUTED
|
||||
} from './actionTypes';
|
||||
|
||||
// @ts-ignore
|
||||
import { isRoomValid } from './functions';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
@@ -38,13 +46,34 @@ const DEFAULT_STATE = {
|
||||
passwordRequired: undefined
|
||||
};
|
||||
|
||||
export interface IConferenceState {
|
||||
conference: Object|undefined;
|
||||
e2eeSupported: boolean|undefined;
|
||||
joining: Object|undefined;
|
||||
leaving: Object|undefined;
|
||||
locked: string|undefined;
|
||||
membersOnly: boolean|undefined;
|
||||
password: string|undefined;
|
||||
passwordRequired: boolean|undefined;
|
||||
authEnabled?: boolean|undefined;
|
||||
authLogin?: string|undefined;
|
||||
subject?: string;
|
||||
localSubject?: string;
|
||||
conferenceTimestamp?: number;
|
||||
authRequired?: Object;
|
||||
followMeEnabled?: boolean;
|
||||
startReactionsMuted?: boolean;
|
||||
room?: Object;
|
||||
pendingSubjectChange?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for actions that contain the conference object, so that it can be
|
||||
* stored for use by other action creators.
|
||||
*/
|
||||
ReducerRegistry.register(
|
||||
'features/base/conference',
|
||||
(state = DEFAULT_STATE, action) => {
|
||||
(state: IConferenceState = DEFAULT_STATE, action: any) => {
|
||||
switch (action.type) {
|
||||
case AUTH_STATUS_CHANGED:
|
||||
return _authStatusChanged(state, action);
|
||||
@@ -125,7 +154,7 @@ ReducerRegistry.register(
|
||||
* @returns {Object} The new state of the feature base/conference after the
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _authStatusChanged(state, { authEnabled, authLogin }) {
|
||||
function _authStatusChanged(state: any, { authEnabled, authLogin }: {authEnabled: boolean, authLogin: string}) {
|
||||
return assign(state, {
|
||||
authEnabled,
|
||||
authLogin
|
||||
@@ -142,7 +171,7 @@ function _authStatusChanged(state, { authEnabled, authLogin }) {
|
||||
* @returns {Object} The new state of the feature base/conference after the
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _conferenceFailed(state, { conference, error }) {
|
||||
function _conferenceFailed(state: any, { conference, error }: {conference: Object, error: any}) {
|
||||
// The current (similar to getCurrentConference in
|
||||
// base/conference/functions.any.js) conference which is joining or joined:
|
||||
const conference_ = state.conference || state.joining;
|
||||
@@ -208,7 +237,7 @@ function _conferenceFailed(state, { conference, error }) {
|
||||
* @returns {Object} The new state of the feature base/conference after the
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _conferenceJoined(state, { conference }) {
|
||||
function _conferenceJoined(state: any, { conference }: {conference: any}) {
|
||||
// FIXME The indicator which determines whether a JitsiConference is locked
|
||||
// i.e. password-protected is private to lib-jitsi-meet. However, the
|
||||
// library does not fire LOCK_STATE_CHANGED upon joining a JitsiConference
|
||||
@@ -254,7 +283,7 @@ function _conferenceJoined(state, { conference }) {
|
||||
* @returns {Object} The next/new state of the feature base/conference after the
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _conferenceLeftOrWillLeave(state, { conference, type }) {
|
||||
function _conferenceLeftOrWillLeave(state: any, { conference, type }: {conference: Object, type: string}) {
|
||||
const nextState = { ...state };
|
||||
|
||||
// The redux action CONFERENCE_LEFT is the last time that we should be
|
||||
@@ -308,7 +337,7 @@ function _conferenceLeftOrWillLeave(state, { conference, type }) {
|
||||
* @returns {Object} The new state of the feature base/conference after the
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _conferenceWillJoin(state, { conference }) {
|
||||
function _conferenceWillJoin(state: any, { conference }: {conference: Object}) {
|
||||
return assign(state, {
|
||||
error: undefined,
|
||||
joining: conference
|
||||
@@ -325,7 +354,7 @@ function _conferenceWillJoin(state, { conference }) {
|
||||
* @returns {Object} The new state of the feature base/conference after the
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _lockStateChanged(state, { conference, locked }) {
|
||||
function _lockStateChanged(state: any, { conference, locked }: {conference: Object, locked: boolean}) {
|
||||
if (state.conference !== conference) {
|
||||
return state;
|
||||
}
|
||||
@@ -346,7 +375,7 @@ function _lockStateChanged(state, { conference, locked }) {
|
||||
* @returns {Object} The new state of the feature base/conference after the
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _p2pStatusChanged(state, action) {
|
||||
function _p2pStatusChanged(state: any, action: any) {
|
||||
return set(state, 'p2p', action.p2p);
|
||||
}
|
||||
|
||||
@@ -359,7 +388,8 @@ function _p2pStatusChanged(state, action) {
|
||||
* @returns {Object} The new state of the feature base/conference after the
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _setPassword(state, { conference, method, password }) {
|
||||
function _setPassword(state: any, { conference, method, password }
|
||||
: {conference: any, method: Object, password: string}) {
|
||||
switch (method) {
|
||||
case conference.join:
|
||||
return assign(state, {
|
||||
@@ -406,7 +436,7 @@ function _setPassword(state, { conference, method, password }) {
|
||||
* @returns {Object} The new state of the feature base/conference after the
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _setRoom(state, action) {
|
||||
function _setRoom(state: any, action: any) {
|
||||
let { room } = action;
|
||||
|
||||
if (!isRoomValid(room)) {
|
||||
467
react/features/base/config/configType.ts
Normal file
467
react/features/base/config/configType.ts
Normal file
@@ -0,0 +1,467 @@
|
||||
type ToolbarButtons = 'camera' |
|
||||
'chat' |
|
||||
'closedcaptions' |
|
||||
'desktop' |
|
||||
'dock-iframe' |
|
||||
'download' |
|
||||
'embedmeeting' |
|
||||
'etherpad' |
|
||||
'feedback' |
|
||||
'filmstrip' |
|
||||
'fullscreen' |
|
||||
'hangup' |
|
||||
'help' |
|
||||
'highlight' |
|
||||
'invite' |
|
||||
'linktosalesforce' |
|
||||
'livestreaming' |
|
||||
'microphone' |
|
||||
'participants-pane' |
|
||||
'profile' |
|
||||
'raisehand' |
|
||||
'recording' |
|
||||
'security' |
|
||||
'select-background' |
|
||||
'settings' |
|
||||
'shareaudio' |
|
||||
'sharedvideo' |
|
||||
'shortcuts' |
|
||||
'stats' |
|
||||
'tileview' |
|
||||
'toggle-camera' |
|
||||
'undock-iframe' |
|
||||
'videoquality' |
|
||||
'__end';
|
||||
|
||||
type ButtonsWithNotifyClick = 'camera' |
|
||||
'chat' |
|
||||
'closedcaptions' |
|
||||
'desktop' |
|
||||
'download' |
|
||||
'embedmeeting' |
|
||||
'etherpad' |
|
||||
'feedback' |
|
||||
'filmstrip' |
|
||||
'fullscreen' |
|
||||
'hangup' |
|
||||
'help' |
|
||||
'invite' |
|
||||
'livestreaming' |
|
||||
'microphone' |
|
||||
'mute-everyone' |
|
||||
'mute-video-everyone' |
|
||||
'participants-pane' |
|
||||
'profile' |
|
||||
'raisehand' |
|
||||
'recording' |
|
||||
'security' |
|
||||
'select-background' |
|
||||
'settings' |
|
||||
'shareaudio' |
|
||||
'sharedvideo' |
|
||||
'shortcuts' |
|
||||
'stats' |
|
||||
'tileview' |
|
||||
'toggle-camera' |
|
||||
'videoquality' |
|
||||
'add-passcode' |
|
||||
'__end';
|
||||
|
||||
type Sounds = 'ASKED_TO_UNMUTE_SOUND' |
|
||||
'E2EE_OFF_SOUND' |
|
||||
'E2EE_ON_SOUND' |
|
||||
'INCOMING_MSG_SOUND' |
|
||||
'KNOCKING_PARTICIPANT_SOUND' |
|
||||
'LIVE_STREAMING_OFF_SOUND' |
|
||||
'LIVE_STREAMING_ON_SOUND' |
|
||||
'NO_AUDIO_SIGNAL_SOUND' |
|
||||
'NOISY_AUDIO_INPUT_SOUND' |
|
||||
'OUTGOING_CALL_EXPIRED_SOUND' |
|
||||
'OUTGOING_CALL_REJECTED_SOUND' |
|
||||
'OUTGOING_CALL_RINGING_SOUND' |
|
||||
'OUTGOING_CALL_START_SOUND' |
|
||||
'PARTICIPANT_JOINED_SOUND' |
|
||||
'PARTICIPANT_LEFT_SOUND' |
|
||||
'RAISE_HAND_SOUND' |
|
||||
'REACTION_SOUND' |
|
||||
'RECORDING_OFF_SOUND' |
|
||||
'RECORDING_ON_SOUND' |
|
||||
'TALK_WHILE_MUTED_SOUND';
|
||||
|
||||
export interface IConfig {
|
||||
hosts?: {
|
||||
domain: string;
|
||||
anonymousdomain?: string;
|
||||
authdomain?: string;
|
||||
focus?: string;
|
||||
muc: string;
|
||||
};
|
||||
bosh?: string;
|
||||
websocket?: string;
|
||||
focusUserJid?: string;
|
||||
testing?: {
|
||||
disableE2EE?: boolean;
|
||||
enableThumbnailReordering?: boolean;
|
||||
mobileXmppWsThreshold?: number;
|
||||
p2pTestMode?: boolean;
|
||||
testMode?: boolean;
|
||||
noAutoPlayVideo?: boolean;
|
||||
capScreenshareBitrate?: number;
|
||||
setScreenSharingResolutionConstraints?: boolean;
|
||||
callStatsThreshold?: number;
|
||||
};
|
||||
flags?: {
|
||||
sourceNameSignaling?: boolean;
|
||||
sendMultipleVideoStreams?: boolean;
|
||||
};
|
||||
disableModeratorIndicator?: boolean;
|
||||
disableReactions?: boolean;
|
||||
disableReactionsModeration?: boolean;
|
||||
disablePolls?: boolean;
|
||||
disableSelfView?: boolean;
|
||||
disableSelfViewSettings?: boolean;
|
||||
screenshotCapture?: {
|
||||
enabled?: boolean;
|
||||
mode?: 'always' | 'recording';
|
||||
};
|
||||
webrtcIceUdpDisable?: boolean;
|
||||
webrtcIceTcpDisable?: boolean;
|
||||
enableUnifiedOnChrome?: boolean;
|
||||
disableAudioLevels?: boolean;
|
||||
audioLevelsInterval?: number;
|
||||
enableNoAudioDetection?: boolean;
|
||||
enableSaveLogs?: boolean;
|
||||
disableShowMoreStats?: boolean;
|
||||
enableNoisyMicDetection?: boolean;
|
||||
startAudioOnly?: boolean;
|
||||
startAudioMuted?: boolean;
|
||||
startWithAudioMuted?: boolean;
|
||||
startSilent?: boolean;
|
||||
enableOpusRed?: boolean;
|
||||
audioQuality?: {
|
||||
stereo?: boolean;
|
||||
opusMaxAverageBitrate?: number|null;
|
||||
};
|
||||
stereo?: boolean;
|
||||
opusMaxAverageBitrate?: number;
|
||||
resolution?: number;
|
||||
disableRemoveRaisedHandOnFocus?: boolean;
|
||||
disableSpeakerStatsSearch?: boolean;
|
||||
speakerStatsOrder?: Array<'role'|'name'|'hasLeft'>;
|
||||
maxFullResolutionParticipants?: number;
|
||||
constraints?: {
|
||||
video?: {
|
||||
height?: {
|
||||
ideal?: number;
|
||||
max?: number;
|
||||
min?: number;
|
||||
}
|
||||
}
|
||||
};
|
||||
disableSimulcast?: boolean;
|
||||
enableLayerSuspension?: boolean;
|
||||
startVideoMuted?: number;
|
||||
startWithVideoMuted?: boolean;
|
||||
preferH264?: boolean;
|
||||
disableH264?: boolean;
|
||||
desktopSharingFrameRate?: {
|
||||
min?: number;
|
||||
max?: number;
|
||||
};
|
||||
startScreenSharing?: boolean;
|
||||
fileRecordingsEnabled?: boolean;
|
||||
dropbox?: {
|
||||
appKey: string;
|
||||
redirectURI?: string;
|
||||
};
|
||||
recordingService?: {
|
||||
enabled?: boolean;
|
||||
sharingEnabled?: boolean;
|
||||
hideStorageWarning?: boolean;
|
||||
};
|
||||
fileRecordingsServiceEnabled?: boolean;
|
||||
fileRecordingsServiceSharingEnabled?: boolean;
|
||||
liveStreamingEnabled?: boolean;
|
||||
localRecording?: {
|
||||
disable?: boolean;
|
||||
notifyAllParticipants?: boolean;
|
||||
};
|
||||
transcribingEnabled?: boolean;
|
||||
transcribeWithAppLanguage?: boolean;
|
||||
preferredTranscribeLanguage?: string;
|
||||
autoCaptionOnRecord?: boolean;
|
||||
transcription?: {
|
||||
enabled?: boolean;
|
||||
useAppLanguage?: boolean;
|
||||
preferredLanguage?: string;
|
||||
disableStartForAll?: boolean;
|
||||
autoCaptionOnRecord?: boolean;
|
||||
};
|
||||
channelLastN?: number;
|
||||
connectionIndicators?: {
|
||||
autoHide?: boolean;
|
||||
autoHideTimeout?: number;
|
||||
disabled?: boolean;
|
||||
disableDetails?: boolean;
|
||||
inactiveDisabled?: boolean;
|
||||
};
|
||||
startLastN?: number;
|
||||
lastNLimits?: {
|
||||
[key: number]: number;
|
||||
};
|
||||
useNewBandwidthAllocationStrategy?: boolean;
|
||||
videoQuality?: {
|
||||
disabledCodec?: string;
|
||||
preferredCodec?: string;
|
||||
enforcePreferredCodec?: boolean;
|
||||
maxBitratesVideo?: {
|
||||
[key: string]: {
|
||||
low?: number;
|
||||
standard?: number;
|
||||
high?: number;
|
||||
}
|
||||
};
|
||||
minHeightForQualityLvl: {
|
||||
[key: number]: string;
|
||||
};
|
||||
resizeDesktopForPresenter?: boolean;
|
||||
};
|
||||
notificationTimeouts?: {
|
||||
short?: number;
|
||||
medium?: number;
|
||||
long?: number;
|
||||
};
|
||||
recordingLimit?: {
|
||||
limit?: number;
|
||||
appName?: string;
|
||||
appURL?: string;
|
||||
};
|
||||
disableRtx?: boolean;
|
||||
disableBeforeUnloadHandlers?: boolean;
|
||||
enableTcc?: boolean;
|
||||
enableRemb?: boolean;
|
||||
enableIceRestart?: boolean;
|
||||
enableForcedReload?: boolean;
|
||||
useTurnUdp?: boolean;
|
||||
enableEncodedTransformSupport?: boolean;
|
||||
disableResponsiveTiles?: boolean;
|
||||
hideLobbyButton?: boolean;
|
||||
autoKnockLobby?: boolean;
|
||||
enableLobbyChat?: boolean;
|
||||
hideAddRoomButton?: boolean;
|
||||
requireDisplayName?: boolean;
|
||||
enableWelcomePage?: boolean;
|
||||
disableShortcuts?: boolean;
|
||||
disableInitialGUM?: boolean;
|
||||
enableClosePage?: boolean;
|
||||
disable1On1Mode?: boolean|null;
|
||||
defaultLocalDisplayName?: string;
|
||||
defaultRemoteDisplayName?: string;
|
||||
hideDisplayName?: boolean;
|
||||
hideDominantSpeakerBadge?: boolean;
|
||||
defaultLanguage?: string;
|
||||
disableProfile?: boolean;
|
||||
hideEmailInSettings?: boolean;
|
||||
enableFeaturesBasedOnToken?: boolean;
|
||||
roomPasswordNumberOfDigits?: number;
|
||||
noticeMessage?: string;
|
||||
enableCalendarIntegration?: boolean;
|
||||
prejoinConfig?: {
|
||||
enabled?: boolean;
|
||||
hideDisplayName?: boolean;
|
||||
hideExtraJoinButtons?: Array<string>;
|
||||
};
|
||||
prejoinPageEnabled?: boolean;
|
||||
readOnlyName?: boolean;
|
||||
openSharedDocumentOnJoin?: boolean;
|
||||
enableInsecureRoomNameWarning?: boolean;
|
||||
enableAutomaticUrlCopy?: boolean;
|
||||
corsAvatarURLs?: Array<string>;
|
||||
gravatarBaseURL?: string;
|
||||
gravatar?: {
|
||||
baseUrl?: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
inviteAppName?: string|null;
|
||||
toolbarButtons?: Array<ToolbarButtons>;
|
||||
toolbarConfig?: {
|
||||
initialTimeout?: number;
|
||||
timeout?: number;
|
||||
alwaysVisible?: boolean;
|
||||
autoHideWhileChatIsOpen?: boolean;
|
||||
};
|
||||
buttonsWithNotifyClick?: Array<ButtonsWithNotifyClick | { key: ButtonsWithNotifyClick; preventExecution: boolean }>;
|
||||
hiddenPremeetingButtons?: Array<'microphone' | 'camera' | 'select-background' | 'invite' | 'settings'>;
|
||||
gatherStats?: boolean;
|
||||
pcStatsInterval?: number;
|
||||
callStatsID?: string;
|
||||
callStatsSecret?: string;
|
||||
callStatsConfigParams?: {
|
||||
disableBeforeUnloadHandler?: boolean;
|
||||
applicationVersion?: string;
|
||||
disablePrecalltest?: boolean;
|
||||
siteID?: string;
|
||||
additionalIDs?: {
|
||||
customerID?: string;
|
||||
tenantID?: string;
|
||||
productName?: string;
|
||||
meetingsName?: string;
|
||||
serverName?: string;
|
||||
pbxID?: string;
|
||||
pbxExtensionID?: string;
|
||||
fqExtensionID?: string;
|
||||
sessionID?: string;
|
||||
};
|
||||
collectLegacyStats?: boolean;
|
||||
collectIP?: boolean;
|
||||
};
|
||||
enableDisplayNameInStats?: boolean;
|
||||
enableEmailInStats?: boolean;
|
||||
faceLandmarks?: {
|
||||
enableFaceCentering?: boolean;
|
||||
enableFaceExpressionsDetection?: boolean;
|
||||
enableDisplayFaceExpressions?: boolean;
|
||||
enableRTCStats?: boolean;
|
||||
faceCenteringThreshold?: number;
|
||||
captureInterval?: number;
|
||||
};
|
||||
feedbackPercentage?: number;
|
||||
disableThirdPartyRequests?: boolean;
|
||||
p2p?: {
|
||||
enabled?: boolean;
|
||||
enableUnifiedOnChrome?: boolean;
|
||||
iceTransportPolicy?: string;
|
||||
preferH264?: boolean;
|
||||
preferredCodec?: string;
|
||||
disableH264?: boolean;
|
||||
disabledCodec?: string;
|
||||
backToP2PDelay?: number;
|
||||
stunServers?: Array<{urls: string}>;
|
||||
};
|
||||
analytics?: {
|
||||
disabled?: boolean;
|
||||
googleAnalyticsTrackingId?: string;
|
||||
matomoEndpoint?: string;
|
||||
matomoSiteID?: string;
|
||||
amplitudeAPPKey?: string;
|
||||
obfuscateRoomName?: boolean;
|
||||
rtcstatsEnabled?: boolean;
|
||||
rtcstatsEndpoint?: string;
|
||||
rtcstatsPolIInterval?: number;
|
||||
scriptURLs?: Array<string>;
|
||||
};
|
||||
apiLogLevels?: Array<'warn' | 'log' | 'error' | 'info' | 'debug'>;
|
||||
deploymentInfo?: {
|
||||
shard?: string;
|
||||
region?: string;
|
||||
userRegion?: string;
|
||||
};
|
||||
disabledSounds?: Array<Sounds>;
|
||||
disableRecordAudioNotification?: boolean;
|
||||
disableJoinLeaveSounds?: boolean;
|
||||
disableIncomingMessageSound?: boolean;
|
||||
chromeExtensionBanner?: {
|
||||
url?: string;
|
||||
edgeUrl?: string;
|
||||
chromeExtensionsInfo?: Array<{id: string; path: string}>;
|
||||
};
|
||||
e2ee?: {
|
||||
labels?: {
|
||||
tooltip?: string;
|
||||
description?: string;
|
||||
label?: string;
|
||||
warning?: string;
|
||||
};
|
||||
externallyManagedKey?: boolean;
|
||||
e2eeLabels?: {
|
||||
tooltip?: string;
|
||||
description?: string;
|
||||
label?: string;
|
||||
warning?: string;
|
||||
};
|
||||
};
|
||||
e2eeLabels?: {
|
||||
tooltip?: string;
|
||||
description?: string;
|
||||
label?: string;
|
||||
warning?: string;
|
||||
};
|
||||
e2eping?: {
|
||||
enabled?: boolean;
|
||||
numRequests?: number;
|
||||
maxConferenceSize?: number;
|
||||
maxMessagesPerSecond?: number;
|
||||
};
|
||||
_desktopSharingSourceDevice?: string;
|
||||
disableDeepLinking?: boolean;
|
||||
disableLocalVideoFlip?: boolean;
|
||||
doNotFlipLocalVideo?: boolean;
|
||||
disableInviteFunctions?: boolean;
|
||||
doNotStoreRoom?: boolean;
|
||||
deploymentUrls?: {
|
||||
userDocumentationURL?: string;
|
||||
downloadAppsUrl?: string;
|
||||
};
|
||||
remoteVideoMenu?: {
|
||||
disabled?: boolean;
|
||||
disableKick?: boolean;
|
||||
disableGrantModerator?: boolean;
|
||||
disablePrivateChat?: boolean;
|
||||
};
|
||||
salesforceUrl?: string;
|
||||
disableRemoteMute?: boolean;
|
||||
enableLipSync?: boolean;
|
||||
dynamicBrandingUrl?: string;
|
||||
participantsPane?: {
|
||||
hideModeratorSettingsTab?: boolean;
|
||||
hideMoreActionsButton?: boolean;
|
||||
hideMuteAllButton?: boolean;
|
||||
};
|
||||
breakoutRooms?: {
|
||||
hideAddRoomButton?: boolean;
|
||||
hideAutoAssignButton?: boolean;
|
||||
hideJoinRoomButton?: boolean;
|
||||
};
|
||||
disableAddingBackgroundImages?: boolean;
|
||||
disableScreensharingVirtualBackground?: boolean;
|
||||
backgroundAlpha?: number;
|
||||
moderatedRoomServiceUrl?: string;
|
||||
disableTileView?: boolean;
|
||||
disableTileEnlargement?: boolean;
|
||||
conferenceInfo?: {
|
||||
alwaysVisible?: Array<string>;
|
||||
autoHide?: Array<string>;
|
||||
};
|
||||
hideConferenceSubject?: boolean;
|
||||
hideConferenceTimer?: boolean;
|
||||
hideRecordingLabel?: boolean;
|
||||
hideParticipantsStats?: boolean;
|
||||
subject?: string;
|
||||
localSubject?: string;
|
||||
useHostPageLocalStorage?: boolean;
|
||||
etherpad_base?: string;
|
||||
dialInNumbersUrl?: string;
|
||||
dialInConfCodeUrl?: string;
|
||||
brandingRoomAlias?: string;
|
||||
mouseMoveCallbackInterval?: number;
|
||||
notifications?: Array<string>;
|
||||
disabledNotifications?: Array<string>;
|
||||
disableFilmstripAutohiding?: boolean;
|
||||
filmstrip?: {
|
||||
disableResizable?: boolean;
|
||||
disableStageFilmstrip?: boolean;
|
||||
disableTopPanel?: boolean;
|
||||
minParticipantCountForTopPanel?: number;
|
||||
};
|
||||
tileView?: {
|
||||
numberOfVisibleTiles?: number;
|
||||
};
|
||||
disableChatSmileys?: boolean;
|
||||
giphy?: {
|
||||
enabled?: boolean;
|
||||
sdkKey?: '';
|
||||
displayMode?: 'all' | 'tile' | 'chat';
|
||||
tileTime?: number;
|
||||
};
|
||||
locationURL?: string;
|
||||
}
|
||||
@@ -222,6 +222,7 @@ export default [
|
||||
'toolbarConfig',
|
||||
'tileView',
|
||||
'transcribingEnabled',
|
||||
'transcription',
|
||||
'useHostPageLocalStorage',
|
||||
'useTurnUdp',
|
||||
'videoQuality',
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
// @flow
|
||||
|
||||
/* eslint-disable import/order */
|
||||
import _ from 'lodash';
|
||||
|
||||
import { CONFERENCE_INFO } from '../../conference/components/constants';
|
||||
import { equals, ReducerRegistry } from '../redux';
|
||||
|
||||
import { equals } from '../redux/functions';
|
||||
import ReducerRegistry from '../redux/ReducerRegistry';
|
||||
|
||||
import {
|
||||
UPDATE_CONFIG,
|
||||
@@ -12,9 +13,12 @@ import {
|
||||
SET_CONFIG,
|
||||
OVERWRITE_CONFIG
|
||||
} from './actionTypes';
|
||||
import { IConfig } from './configType';
|
||||
|
||||
// @ts-ignore
|
||||
import { _cleanupConfig } from './functions';
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
declare let interfaceConfig: any;
|
||||
|
||||
/**
|
||||
* The initial state of the feature base/config when executing in a
|
||||
@@ -26,7 +30,7 @@ declare var interfaceConfig: Object;
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const INITIAL_NON_RN_STATE = {
|
||||
const INITIAL_NON_RN_STATE: IConfig = {
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -38,7 +42,7 @@ const INITIAL_NON_RN_STATE = {
|
||||
*
|
||||
* @type {Object}
|
||||
*/
|
||||
const INITIAL_RN_STATE = {
|
||||
const INITIAL_RN_STATE: IConfig = {
|
||||
analytics: {},
|
||||
|
||||
// FIXME The support for audio levels in lib-jitsi-meet polls the statistics
|
||||
@@ -61,14 +65,14 @@ const INITIAL_RN_STATE = {
|
||||
* Mapping between old configs controlling the conference info headers visibility and the
|
||||
* new configs. Needed in order to keep backwards compatibility.
|
||||
*/
|
||||
const CONFERENCE_HEADER_MAPPING = {
|
||||
const CONFERENCE_HEADER_MAPPING: any = {
|
||||
hideConferenceTimer: [ 'conference-timer' ],
|
||||
hideConferenceSubject: [ 'subject' ],
|
||||
hideParticipantsStats: [ 'participants-count' ],
|
||||
hideRecordingLabel: [ 'recording' ]
|
||||
};
|
||||
|
||||
ReducerRegistry.register('features/base/config', (state = _getInitialState(), action) => {
|
||||
ReducerRegistry.register('features/base/config', (state: IConfig = _getInitialState(), action: any) => {
|
||||
switch (action.type) {
|
||||
case UPDATE_CONFIG:
|
||||
return _updateConfig(state, action);
|
||||
@@ -139,12 +143,12 @@ function _getInitialState() {
|
||||
* Reduces a specific Redux action SET_CONFIG of the feature
|
||||
* base/lib-jitsi-meet.
|
||||
*
|
||||
* @param {Object} state - The Redux state of the feature base/config.
|
||||
* @param {IConfig} state - The Redux state of the feature base/config.
|
||||
* @param {Action} action - The Redux action SET_CONFIG to reduce.
|
||||
* @private
|
||||
* @returns {Object} The new state after the reduction of the specified action.
|
||||
*/
|
||||
function _setConfig(state, { config }) {
|
||||
function _setConfig(state: IConfig, { config }: {config: IConfig}) {
|
||||
// The mobile app bundles jitsi-meet and lib-jitsi-meet at build time and
|
||||
// does not download them at runtime from the deployment on which it will
|
||||
// join a conference. The downloading is planned for implementation in the
|
||||
@@ -187,10 +191,10 @@ function _setConfig(state, { config }) {
|
||||
/**
|
||||
* Processes the conferenceInfo object against the defaults.
|
||||
*
|
||||
* @param {Object} config - The old config.
|
||||
* @param {IConfig} config - The old config.
|
||||
* @returns {Object} The processed conferenceInfo object.
|
||||
*/
|
||||
function _getConferenceInfo(config) {
|
||||
function _getConferenceInfo(config: IConfig) {
|
||||
const { conferenceInfo } = config;
|
||||
|
||||
if (conferenceInfo) {
|
||||
@@ -207,11 +211,7 @@ function _getConferenceInfo(config) {
|
||||
|
||||
/**
|
||||
* Constructs a new config {@code Object}, if necessary, out of a specific
|
||||
* config {@code Object} which is in the latest format supported by jitsi-meet.
|
||||
* Such a translation from an old config format to a new/the latest config
|
||||
* format is necessary because the mobile app bundles jitsi-meet and
|
||||
* lib-jitsi-meet at build time and does not download them at runtime from the
|
||||
* deployment on which it will join a conference.
|
||||
* interface_config {@code Object} which is in the latest format supported by jitsi-meet.
|
||||
*
|
||||
* @param {Object} oldValue - The config {@code Object} which may or may not be
|
||||
* in the latest form supported by jitsi-meet and from which a new config
|
||||
@@ -219,11 +219,11 @@ function _getConferenceInfo(config) {
|
||||
* @returns {Object} A config {@code Object} which is in the latest format
|
||||
* supported by jitsi-meet.
|
||||
*/
|
||||
function _translateLegacyConfig(oldValue: Object) {
|
||||
function _translateInterfaceConfig(oldValue: IConfig) {
|
||||
const newValue = oldValue;
|
||||
|
||||
if (!Array.isArray(oldValue.toolbarButtons)
|
||||
&& typeof interfaceConfig === 'object' && Array.isArray(interfaceConfig.TOOLBAR_BUTTONS)) {
|
||||
&& typeof interfaceConfig === 'object' && Array.isArray(interfaceConfig.TOOLBAR_BUTTONS)) {
|
||||
newValue.toolbarButtons = interfaceConfig.TOOLBAR_BUTTONS;
|
||||
}
|
||||
|
||||
@@ -231,6 +231,7 @@ function _translateLegacyConfig(oldValue: Object) {
|
||||
oldValue.toolbarConfig = {};
|
||||
}
|
||||
|
||||
newValue.toolbarConfig = oldValue.toolbarConfig || {};
|
||||
if (typeof oldValue.toolbarConfig.alwaysVisible !== 'boolean'
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& typeof interfaceConfig.TOOLBAR_ALWAYS_VISIBLE === 'boolean') {
|
||||
@@ -249,32 +250,11 @@ function _translateLegacyConfig(oldValue: Object) {
|
||||
newValue.toolbarConfig.timeout = interfaceConfig.TOOLBAR_TIMEOUT;
|
||||
}
|
||||
|
||||
const filteredConferenceInfo = Object.keys(CONFERENCE_HEADER_MAPPING).filter(key => oldValue[key]);
|
||||
|
||||
if (filteredConferenceInfo.length) {
|
||||
newValue.conferenceInfo = _getConferenceInfo(oldValue);
|
||||
|
||||
filteredConferenceInfo.forEach(key => {
|
||||
// hideRecordingLabel does not mean not render it at all, but autoHide it
|
||||
if (key === 'hideRecordingLabel') {
|
||||
newValue.conferenceInfo.alwaysVisible
|
||||
= newValue.conferenceInfo.alwaysVisible.filter(c => !CONFERENCE_HEADER_MAPPING[key].includes(c));
|
||||
newValue.conferenceInfo.autoHide
|
||||
= _.union(newValue.conferenceInfo.autoHide, CONFERENCE_HEADER_MAPPING[key]);
|
||||
} else {
|
||||
newValue.conferenceInfo.alwaysVisible
|
||||
= newValue.conferenceInfo.alwaysVisible.filter(c => !CONFERENCE_HEADER_MAPPING[key].includes(c));
|
||||
newValue.conferenceInfo.autoHide
|
||||
= newValue.conferenceInfo.autoHide.filter(c => !CONFERENCE_HEADER_MAPPING[key].includes(c));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!oldValue.connectionIndicators
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& (interfaceConfig.hasOwnProperty('CONNECTION_INDICATOR_DISABLED')
|
||||
|| interfaceConfig.hasOwnProperty('CONNECTION_INDICATOR_AUTO_HIDE_ENABLED')
|
||||
|| interfaceConfig.hasOwnProperty('CONNECTION_INDICATOR_AUTO_HIDE_TIMEOUT'))) {
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& (interfaceConfig.hasOwnProperty('CONNECTION_INDICATOR_DISABLED')
|
||||
|| interfaceConfig.hasOwnProperty('CONNECTION_INDICATOR_AUTO_HIDE_ENABLED')
|
||||
|| interfaceConfig.hasOwnProperty('CONNECTION_INDICATOR_AUTO_HIDE_TIMEOUT'))) {
|
||||
newValue.connectionIndicators = {
|
||||
disabled: interfaceConfig.CONNECTION_INDICATOR_DISABLED,
|
||||
autoHide: interfaceConfig.CONNECTION_INDICATOR_AUTO_HIDE_ENABLED,
|
||||
@@ -282,6 +262,71 @@ function _translateLegacyConfig(oldValue: Object) {
|
||||
};
|
||||
}
|
||||
|
||||
if (oldValue.disableModeratorIndicator === undefined
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& interfaceConfig.hasOwnProperty('DISABLE_FOCUS_INDICATOR')) {
|
||||
newValue.disableModeratorIndicator = interfaceConfig.DISABLE_FOCUS_INDICATOR;
|
||||
}
|
||||
|
||||
if (oldValue.defaultLocalDisplayName === undefined
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& interfaceConfig.hasOwnProperty('DEFAULT_LOCAL_DISPLAY_NAME')) {
|
||||
newValue.defaultLocalDisplayName = interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME;
|
||||
}
|
||||
|
||||
if (oldValue.defaultRemoteDisplayName === undefined
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& interfaceConfig.hasOwnProperty('DEFAULT_REMOTE_DISPLAY_NAME')) {
|
||||
newValue.defaultRemoteDisplayName = interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
|
||||
}
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new config {@code Object}, if necessary, out of a specific
|
||||
* config {@code Object} which is in the latest format supported by jitsi-meet.
|
||||
* Such a translation from an old config format to a new/the latest config
|
||||
* format is necessary because the mobile app bundles jitsi-meet and
|
||||
* lib-jitsi-meet at build time and does not download them at runtime from the
|
||||
* deployment on which it will join a conference.
|
||||
*
|
||||
* @param {Object} oldValue - The config {@code Object} which may or may not be
|
||||
* in the latest form supported by jitsi-meet and from which a new config
|
||||
* {@code Object} is to be constructed if necessary.
|
||||
* @returns {Object} A config {@code Object} which is in the latest format
|
||||
* supported by jitsi-meet.
|
||||
*/
|
||||
function _translateLegacyConfig(oldValue: IConfig) {
|
||||
const newValue = _translateInterfaceConfig(oldValue);
|
||||
|
||||
// Translate deprecated config values to new config values.
|
||||
|
||||
const filteredConferenceInfo = Object.keys(CONFERENCE_HEADER_MAPPING).filter(key => oldValue[key as keyof IConfig]);
|
||||
|
||||
if (filteredConferenceInfo.length) {
|
||||
newValue.conferenceInfo = _getConferenceInfo(oldValue);
|
||||
|
||||
filteredConferenceInfo.forEach(key => {
|
||||
newValue.conferenceInfo = oldValue.conferenceInfo ?? {};
|
||||
|
||||
// hideRecordingLabel does not mean not render it at all, but autoHide it
|
||||
if (key === 'hideRecordingLabel') {
|
||||
newValue.conferenceInfo.alwaysVisible
|
||||
= (newValue.conferenceInfo?.alwaysVisible ?? [])
|
||||
.filter(c => !CONFERENCE_HEADER_MAPPING[key].includes(c));
|
||||
newValue.conferenceInfo.autoHide
|
||||
= _.union(newValue.conferenceInfo.autoHide, CONFERENCE_HEADER_MAPPING[key]);
|
||||
} else {
|
||||
newValue.conferenceInfo.alwaysVisible
|
||||
= (newValue.conferenceInfo.alwaysVisible ?? [])
|
||||
.filter(c => !CONFERENCE_HEADER_MAPPING[key].includes(c));
|
||||
newValue.conferenceInfo.autoHide
|
||||
= (newValue.conferenceInfo.autoHide ?? []).filter(c => !CONFERENCE_HEADER_MAPPING[key].includes(c));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
newValue.prejoinConfig = oldValue.prejoinConfig || {};
|
||||
if (oldValue.hasOwnProperty('prejoinPageEnabled')
|
||||
&& !newValue.prejoinConfig.hasOwnProperty('enabled')
|
||||
@@ -315,33 +360,15 @@ function _translateLegacyConfig(oldValue: Object) {
|
||||
};
|
||||
}
|
||||
|
||||
if (oldValue.disableModeratorIndicator === undefined
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& interfaceConfig.hasOwnProperty('DISABLE_FOCUS_INDICATOR')) {
|
||||
newValue.disableModeratorIndicator = interfaceConfig.DISABLE_FOCUS_INDICATOR;
|
||||
}
|
||||
|
||||
newValue.e2ee = newValue.e2ee || {};
|
||||
|
||||
if (oldValue.e2eeLabels) {
|
||||
newValue.e2ee.e2eeLabels = oldValue.e2eeLabels;
|
||||
}
|
||||
|
||||
if (oldValue.defaultLocalDisplayName === undefined
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& interfaceConfig.hasOwnProperty('DEFAULT_LOCAL_DISPLAY_NAME')) {
|
||||
newValue.defaultLocalDisplayName = interfaceConfig.DEFAULT_LOCAL_DISPLAY_NAME;
|
||||
}
|
||||
|
||||
newValue.defaultLocalDisplayName
|
||||
= newValue.defaultLocalDisplayName || 'me';
|
||||
|
||||
if (oldValue.defaultRemoteDisplayName === undefined
|
||||
&& typeof interfaceConfig === 'object'
|
||||
&& interfaceConfig.hasOwnProperty('DEFAULT_REMOTE_DISPLAY_NAME')) {
|
||||
newValue.defaultRemoteDisplayName = interfaceConfig.DEFAULT_REMOTE_DISPLAY_NAME;
|
||||
}
|
||||
|
||||
if (oldValue.hideAddRoomButton) {
|
||||
newValue.breakoutRooms = {
|
||||
/* eslint-disable-next-line no-extra-parens */
|
||||
@@ -353,6 +380,46 @@ function _translateLegacyConfig(oldValue: Object) {
|
||||
newValue.defaultRemoteDisplayName
|
||||
= newValue.defaultRemoteDisplayName || 'Fellow Jitster';
|
||||
|
||||
newValue.transcription = newValue.transcription || {};
|
||||
if (oldValue.transcribingEnabled !== undefined) {
|
||||
newValue.transcription = {
|
||||
...newValue.transcription,
|
||||
enabled: oldValue.transcribingEnabled
|
||||
};
|
||||
}
|
||||
if (oldValue.transcribeWithAppLanguage !== undefined) {
|
||||
newValue.transcription = {
|
||||
...newValue.transcription,
|
||||
useAppLanguage: oldValue.transcribeWithAppLanguage
|
||||
};
|
||||
}
|
||||
if (oldValue.preferredTranscribeLanguage !== undefined) {
|
||||
newValue.transcription = {
|
||||
...newValue.transcription,
|
||||
preferredLanguage: oldValue.preferredTranscribeLanguage
|
||||
};
|
||||
}
|
||||
if (oldValue.autoCaptionOnRecord !== undefined) {
|
||||
newValue.transcription = {
|
||||
...newValue.transcription,
|
||||
autoCaptionOnRecord: oldValue.autoCaptionOnRecord
|
||||
};
|
||||
}
|
||||
|
||||
newValue.recordingService = newValue.recordingService || {};
|
||||
if (oldValue.fileRecordingsServiceEnabled !== undefined) {
|
||||
newValue.recordingService = {
|
||||
...newValue.recordingService,
|
||||
enabled: oldValue.fileRecordingsServiceEnabled
|
||||
};
|
||||
}
|
||||
if (oldValue.fileRecordingsServiceSharingEnabled !== undefined) {
|
||||
newValue.recordingService = {
|
||||
...newValue.recordingService,
|
||||
sharingEnabled: oldValue.fileRecordingsServiceSharingEnabled
|
||||
};
|
||||
}
|
||||
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@@ -364,7 +431,7 @@ function _translateLegacyConfig(oldValue: Object) {
|
||||
* @private
|
||||
* @returns {Object} The new state after the reduction of the specified action.
|
||||
*/
|
||||
function _updateConfig(state, { config }) {
|
||||
function _updateConfig(state: IConfig, { config }: {config: IConfig}) {
|
||||
const newState = _.merge({}, state, config);
|
||||
|
||||
_cleanupConfig(newState);
|
||||
@@ -1,8 +1,11 @@
|
||||
/* @flow */
|
||||
/* eslint-disable import/order */
|
||||
|
||||
import { SET_ROOM } from '../conference';
|
||||
import { SET_ROOM } from '../conference/actionTypes';
|
||||
|
||||
// @ts-ignore
|
||||
import { JitsiConnectionErrors } from '../lib-jitsi-meet';
|
||||
import { assign, set, ReducerRegistry } from '../redux';
|
||||
import { assign, set } from '../redux/functions';
|
||||
import ReducerRegistry from '../redux/ReducerRegistry';
|
||||
|
||||
import {
|
||||
CONNECTION_DISCONNECTED,
|
||||
@@ -12,14 +15,26 @@ import {
|
||||
SET_LOCATION_URL,
|
||||
SHOW_CONNECTION_INFO
|
||||
} from './actionTypes';
|
||||
import type { ConnectionFailedError } from './actions.native';
|
||||
|
||||
// @ts-ignore
|
||||
import { ConnectionFailedError } from './actions.native';
|
||||
|
||||
export interface IConnectionState {
|
||||
connection?: Object;
|
||||
connecting?: Object;
|
||||
timeEstablished?: number;
|
||||
error?: ConnectionFailedError;
|
||||
passwordRequired?: Object;
|
||||
locationURL?: URL;
|
||||
showConnectionInfo?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduces the Redux actions of the feature base/connection.
|
||||
*/
|
||||
ReducerRegistry.register(
|
||||
'features/base/connection',
|
||||
(state: Object = {}, action: Object) => {
|
||||
(state: IConnectionState = {}, action: any) => {
|
||||
switch (action.type) {
|
||||
case CONNECTION_DISCONNECTED:
|
||||
return _connectionDisconnected(state, action);
|
||||
@@ -50,14 +65,14 @@ ReducerRegistry.register(
|
||||
* Reduces a specific Redux action CONNECTION_DISCONNECTED of the feature
|
||||
* base/connection.
|
||||
*
|
||||
* @param {Object} state - The Redux state of the feature base/connection.
|
||||
* @param {IConnectionState} state - The Redux state of the feature base/connection.
|
||||
* @param {Action} action - The Redux action CONNECTION_DISCONNECTED to reduce.
|
||||
* @private
|
||||
* @returns {Object} The new state of the feature base/connection after the
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _connectionDisconnected(
|
||||
state: Object,
|
||||
state: IConnectionState,
|
||||
{ connection }: { connection: Object }) {
|
||||
const connection_ = _getCurrentConnection(state);
|
||||
|
||||
@@ -76,14 +91,14 @@ function _connectionDisconnected(
|
||||
* Reduces a specific Redux action CONNECTION_ESTABLISHED of the feature
|
||||
* base/connection.
|
||||
*
|
||||
* @param {Object} state - The Redux state of the feature base/connection.
|
||||
* @param {IConnectionState} state - The Redux state of the feature base/connection.
|
||||
* @param {Action} action - The Redux action CONNECTION_ESTABLISHED to reduce.
|
||||
* @private
|
||||
* @returns {Object} The new state of the feature base/connection after the
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _connectionEstablished(
|
||||
state: Object,
|
||||
state: IConnectionState,
|
||||
{ connection, timeEstablished }: {
|
||||
connection: Object,
|
||||
timeEstablished: number
|
||||
@@ -101,14 +116,14 @@ function _connectionEstablished(
|
||||
* Reduces a specific Redux action CONNECTION_FAILED of the feature
|
||||
* base/connection.
|
||||
*
|
||||
* @param {Object} state - The Redux state of the feature base/connection.
|
||||
* @param {IConnectionState} state - The Redux state of the feature base/connection.
|
||||
* @param {Action} action - The Redux action CONNECTION_FAILED to reduce.
|
||||
* @private
|
||||
* @returns {Object} The new state of the feature base/connection after the
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _connectionFailed(
|
||||
state: Object,
|
||||
state: IConnectionState,
|
||||
{ connection, error }: {
|
||||
connection: Object,
|
||||
error: ConnectionFailedError
|
||||
@@ -133,14 +148,14 @@ function _connectionFailed(
|
||||
* Reduces a specific Redux action CONNECTION_WILL_CONNECT of the feature
|
||||
* base/connection.
|
||||
*
|
||||
* @param {Object} state - The Redux state of the feature base/connection.
|
||||
* @param {IConnectionState} state - The Redux state of the feature base/connection.
|
||||
* @param {Action} action - The Redux action CONNECTION_WILL_CONNECT to reduce.
|
||||
* @private
|
||||
* @returns {Object} The new state of the feature base/connection after the
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _connectionWillConnect(
|
||||
state: Object,
|
||||
state: IConnectionState,
|
||||
{ connection }: { connection: Object }) {
|
||||
return assign(state, {
|
||||
connecting: connection,
|
||||
@@ -159,12 +174,12 @@ function _connectionWillConnect(
|
||||
* The current (similar to getCurrentConference in base/conference/functions.any.js)
|
||||
* connection which is {@code connection} or {@code connecting}.
|
||||
*
|
||||
* @param {Object} baseConnectionState - The current state of the
|
||||
* @param {IConnectionState} baseConnectionState - The current state of the
|
||||
* {@code 'base/connection'} feature.
|
||||
* @returns {JitsiConnection} - The current {@code JitsiConnection} if any.
|
||||
* @private
|
||||
*/
|
||||
function _getCurrentConnection(baseConnectionState: Object): ?Object {
|
||||
function _getCurrentConnection(baseConnectionState: IConnectionState): IConnectionState|undefined {
|
||||
return baseConnectionState.connection || baseConnectionState.connecting;
|
||||
}
|
||||
|
||||
@@ -172,15 +187,15 @@ function _getCurrentConnection(baseConnectionState: Object): ?Object {
|
||||
* Reduces a specific redux action {@link SET_LOCATION_URL} of the feature
|
||||
* base/connection.
|
||||
*
|
||||
* @param {Object} state - The redux state of the feature base/connection.
|
||||
* @param {IConnectionState} state - The redux state of the feature base/connection.
|
||||
* @param {Action} action - The redux action {@code SET_LOCATION_URL} to reduce.
|
||||
* @private
|
||||
* @returns {Object} The new state of the feature base/connection after the
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _setLocationURL(
|
||||
state: Object,
|
||||
{ locationURL }: { locationURL: ?URL }) {
|
||||
state: IConnectionState,
|
||||
{ locationURL }: { locationURL?: URL }) {
|
||||
return set(state, 'locationURL', locationURL);
|
||||
}
|
||||
|
||||
@@ -188,12 +203,12 @@ function _setLocationURL(
|
||||
* Reduces a specific redux action {@link SET_ROOM} of the feature
|
||||
* base/connection.
|
||||
*
|
||||
* @param {Object} state - The redux state of the feature base/connection.
|
||||
* @param {IConnectionState} state - The redux state of the feature base/connection.
|
||||
* @private
|
||||
* @returns {Object} The new state of the feature base/connection after the
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _setRoom(state: Object) {
|
||||
function _setRoom(state: IConnectionState) {
|
||||
return assign(state, {
|
||||
error: undefined,
|
||||
passwordRequired: undefined
|
||||
@@ -204,14 +219,14 @@ function _setRoom(state: Object) {
|
||||
* Reduces a specific redux action {@link SHOW_CONNECTION_INFO} of the feature
|
||||
* base/connection.
|
||||
*
|
||||
* @param {Object} state - The redux state of the feature base/connection.
|
||||
* @param {IConnectionState} state - The redux state of the feature base/connection.
|
||||
* @param {Action} action - The redux action {@code SHOW_CONNECTION_INFO} to reduce.
|
||||
* @private
|
||||
* @returns {Object} The new state of the feature base/connection after the
|
||||
* reduction of the specified action.
|
||||
*/
|
||||
function _setShowConnectionInfo(
|
||||
state: Object,
|
||||
state: IConnectionState,
|
||||
{ showConnectionInfo }: { showConnectionInfo: boolean }) {
|
||||
return set(state, 'showConnectionInfo', showConnectionInfo);
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import { ReducerRegistry } from '../redux';
|
||||
/* eslint-disable import/order */
|
||||
import ReducerRegistry from '../redux/ReducerRegistry';
|
||||
|
||||
import {
|
||||
ADD_PENDING_DEVICE_REQUEST,
|
||||
@@ -8,10 +9,15 @@ import {
|
||||
SET_VIDEO_INPUT_DEVICE,
|
||||
UPDATE_DEVICE_LIST
|
||||
} from './actionTypes';
|
||||
|
||||
// @ts-ignore
|
||||
import { groupDevicesByKind } from './functions';
|
||||
|
||||
// @ts-ignore
|
||||
import logger from './logger';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
|
||||
const DEFAULT_STATE: IDevicesState = {
|
||||
availableDevices: {
|
||||
audioInput: [],
|
||||
audioOutput: [],
|
||||
@@ -24,10 +30,23 @@ const DEFAULT_STATE = {
|
||||
}
|
||||
};
|
||||
|
||||
export interface IDevicesState {
|
||||
availableDevices: {
|
||||
audioInput: MediaDeviceInfo[];
|
||||
audioOutput: MediaDeviceInfo[];
|
||||
videoInput: MediaDeviceInfo[];
|
||||
};
|
||||
pendingRequests: Object[];
|
||||
permissions: {
|
||||
audio: boolean;
|
||||
video: boolean;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for actions which changes the state of known and used devices.
|
||||
*
|
||||
* @param {Object} state - The Redux state of the feature features/base/devices.
|
||||
* @param {IDevicesState} state - The Redux state of the feature features/base/devices.
|
||||
* @param {Object} action - Action object.
|
||||
* @param {string} action.type - Type of action.
|
||||
* @param {Array<MediaDeviceInfo>} action.devices - All available audio and
|
||||
@@ -36,7 +55,7 @@ const DEFAULT_STATE = {
|
||||
*/
|
||||
ReducerRegistry.register(
|
||||
'features/base/devices',
|
||||
(state = DEFAULT_STATE, action) => {
|
||||
(state: IDevicesState = DEFAULT_STATE, action) => {
|
||||
switch (action.type) {
|
||||
case UPDATE_DEVICE_LIST: {
|
||||
const deviceList = groupDevicesByKind(action.devices);
|
||||
@@ -2,9 +2,9 @@ import React, { Fragment } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
|
||||
const BottomSheetContainer: () => JSX.Element = () => {
|
||||
const { sheet, sheetProps } = useSelector(state => state['features/base/dialog']);
|
||||
const { reducedUI } = useSelector(state => state['features/base/responsive-ui']);
|
||||
const BottomSheetContainer: () => JSX.Element|null = (): JSX.Element|null => {
|
||||
const { sheet, sheetProps } = useSelector((state: any) => state['features/base/dialog']);
|
||||
const { reducedUI } = useSelector((state: any) => state['features/base/responsive-ui']);
|
||||
|
||||
if (!sheet || reducedUI) {
|
||||
return null;
|
||||
@@ -15,6 +15,6 @@ const BottomSheetContainer: () => JSX.Element = () => {
|
||||
{ React.createElement(sheet, sheetProps) }
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
export default BottomSheetContainer;
|
||||
|
||||
@@ -61,7 +61,7 @@ const styles = theme => {
|
||||
},
|
||||
|
||||
'&:hover': {
|
||||
background: theme.palette.action02
|
||||
background: theme.palette.ui04
|
||||
}
|
||||
},
|
||||
header: {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { assign, ReducerRegistry } from '../redux';
|
||||
import ReducerRegistry from '../redux/ReducerRegistry';
|
||||
import { assign } from '../redux/functions';
|
||||
|
||||
import {
|
||||
HIDE_DIALOG,
|
||||
@@ -7,16 +8,23 @@ import {
|
||||
OPEN_SHEET
|
||||
} from './actionTypes';
|
||||
|
||||
export interface IDialogState {
|
||||
component?: Object;
|
||||
componentProps?: Object;
|
||||
sheet?: Object;
|
||||
sheetProps?: Object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reduces redux actions which show or hide dialogs.
|
||||
*
|
||||
* @param {State} state - The current redux state.
|
||||
* @param {IDialogState} state - The current redux state.
|
||||
* @param {Action} action - The redux action to reduce.
|
||||
* @param {string} action.type - The type of the redux action to reduce..
|
||||
* @returns {State} The next redux state that is the result of reducing the
|
||||
* specified action.
|
||||
*/
|
||||
ReducerRegistry.register('features/base/dialog', (state = {}, action) => {
|
||||
ReducerRegistry.register('features/base/dialog', (state: IDialogState = {}, action) => {
|
||||
switch (action.type) {
|
||||
case HIDE_DIALOG: {
|
||||
const { component } = action;
|
||||
@@ -13,7 +13,7 @@ import { ADD_KNOWN_DOMAINS } from './actionTypes';
|
||||
* read the information out of the app's manifests: App Store strips the
|
||||
* associated domains manifest out of the app so it's never downloaded on the
|
||||
* client and we did not spend a lot of effort to read the associated domains
|
||||
* out of the Andorid manifest.
|
||||
* out of the Android manifest.
|
||||
*/
|
||||
export const DEFAULT_STATE = [
|
||||
'alpha.jitsi.net',
|
||||
|
||||
@@ -24,7 +24,7 @@ export function getLastNForQualityLevel(qualityLevel, channelLastN) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given Object is a correct last N limit mapping, coverts both keys and values to numbers and sorts
|
||||
* Checks if the given Object is a correct last N limit mapping, converts both keys and values to numbers and sorts
|
||||
* the keys in ascending order.
|
||||
*
|
||||
* @param {Object} lastNLimits - The Object to be verified.
|
||||
|
||||
@@ -637,7 +637,7 @@ class VideoTransform extends Component<Props, State> {
|
||||
|
||||
/**
|
||||
* Function to decide whether the responder should respond to a start
|
||||
* (thouch) event.
|
||||
* (touch) event.
|
||||
*
|
||||
* @private
|
||||
* @returns {boolean}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// @flow
|
||||
|
||||
/**
|
||||
* The set of facing modes for camera.
|
||||
*
|
||||
@@ -17,7 +15,7 @@ export type MediaType = 'audio' | 'video' | 'presenter' | 'screenshare';
|
||||
*
|
||||
* @enum {string}
|
||||
*/
|
||||
export const MEDIA_TYPE = {
|
||||
export const MEDIA_TYPE: {[key: string]: MediaType} = {
|
||||
AUDIO: 'audio',
|
||||
PRESENTER: 'presenter',
|
||||
SCREENSHARE: 'screenshare',
|
||||
@@ -116,7 +116,7 @@ export function isVideoMutedByUser(stateful: Function | Object) {
|
||||
* @param {boolean} waitForVideoStarted - True if the specified videoTrack
|
||||
* should be rendered only after its associated video has started;
|
||||
* otherwise, false.
|
||||
* @returns {boolean} True if the specified videoTrack should be renderd;
|
||||
* @returns {boolean} True if the specified videoTrack should be rendered;
|
||||
* otherwise, false.
|
||||
*/
|
||||
export function shouldRenderVideoTrack(
|
||||
|
||||
1
react/features/base/media/middleware.native.ts
Normal file
1
react/features/base/media/middleware.native.ts
Normal file
@@ -0,0 +1 @@
|
||||
import './middleware.any.js';
|
||||
49
react/features/base/media/middleware.web.ts
Normal file
49
react/features/base/media/middleware.web.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
/* eslint-disable import/order */
|
||||
import './middleware.any.js';
|
||||
|
||||
import { IStore } from '../../app/types';
|
||||
|
||||
// @ts-ignore
|
||||
import { NOTIFICATION_TIMEOUT_TYPE, showNotification } from '../../notifications';
|
||||
import LocalRecordingManager from '../../recording/components/Recording/LocalRecordingManager.web';
|
||||
|
||||
// @ts-ignore
|
||||
import StopRecordingDialog from '../../recording/components/Recording/web/StopRecordingDialog';
|
||||
|
||||
// @ts-ignore
|
||||
import { openDialog } from '../dialog';
|
||||
|
||||
// @ts-ignore
|
||||
import { MiddlewareRegistry } from '../redux';
|
||||
|
||||
import { SET_VIDEO_MUTED } from './actionTypes';
|
||||
|
||||
/**
|
||||
* Implements the entry point of the middleware of the feature base/media.
|
||||
*
|
||||
* @param {IStore} store - The redux store.
|
||||
* @returns {Function}
|
||||
*/
|
||||
MiddlewareRegistry.register((store: IStore) => (next: Function) => (action: any) => {
|
||||
const { dispatch } = store;
|
||||
|
||||
switch (action.type) {
|
||||
case SET_VIDEO_MUTED: {
|
||||
if (LocalRecordingManager.isRecordingLocally() && LocalRecordingManager.selfRecording.on) {
|
||||
if (action.muted && LocalRecordingManager.selfRecording.withVideo) {
|
||||
dispatch(openDialog(StopRecordingDialog, { localRecordingVideoStop: true }));
|
||||
|
||||
return;
|
||||
} else if (!action.muted && !LocalRecordingManager.selfRecording.withVideo) {
|
||||
dispatch(showNotification({
|
||||
titleKey: 'recording.localRecordingNoVideo',
|
||||
descriptionKey: 'recording.localRecordingVideoWarning',
|
||||
uid: 'recording.localRecordingNoVideo'
|
||||
}, NOTIFICATION_TIMEOUT_TYPE.MEDIUM));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
||||
});
|
||||
@@ -1,6 +1,6 @@
|
||||
// @flow
|
||||
|
||||
import { getDefaultHeaderHeight } from '@react-navigation/elements';
|
||||
import { useHeaderHeight } from '@react-navigation/elements';
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import {
|
||||
Keyboard,
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
Platform,
|
||||
StatusBar
|
||||
} from 'react-native';
|
||||
import { useSafeAreaFrame, useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
import { StyleType } from '../../styles';
|
||||
|
||||
@@ -34,11 +34,6 @@ type Props = {
|
||||
*/
|
||||
hasTabNavigator: boolean,
|
||||
|
||||
/**
|
||||
* Is the screen presented as a modal?
|
||||
*/
|
||||
isModalPresentation: boolean,
|
||||
|
||||
/**
|
||||
* Additional style to be appended to the KeyboardAvoidingView.
|
||||
*/
|
||||
@@ -51,33 +46,26 @@ const JitsiKeyboardAvoidingView = (
|
||||
contentContainerStyle,
|
||||
hasTabNavigator,
|
||||
hasBottomTextInput,
|
||||
isModalPresentation,
|
||||
style
|
||||
}: Props) => {
|
||||
const frame = useSafeAreaFrame();
|
||||
const headerHeight = useHeaderHeight();
|
||||
const insets = useSafeAreaInsets();
|
||||
const [ bottomPadding, setBottomPadding ] = useState(insets.bottom);
|
||||
const [ topPadding, setTopPadding ] = useState(insets.top);
|
||||
|
||||
useEffect(() => {
|
||||
// This useEffect is needed because insets are undefined at first for some reason
|
||||
// https://github.com/th3rdwave/react-native-safe-area-context/issues/54
|
||||
setBottomPadding(insets.bottom);
|
||||
setTopPadding(insets.top);
|
||||
}, [ insets.bottom, insets.top ]);
|
||||
}, [ insets.bottom ]);
|
||||
|
||||
const headerHeight = getDefaultHeaderHeight(frame, isModalPresentation, topPadding);
|
||||
|
||||
// Notch devices have in general a header height between 103 and 106px
|
||||
const topNotchDevice = headerHeight > 100;
|
||||
const deviceHeight = topNotchDevice ? headerHeight - 50 : headerHeight;
|
||||
const tabNavigatorPadding
|
||||
= hasTabNavigator ? deviceHeight : 0;
|
||||
= hasTabNavigator ? headerHeight : 0;
|
||||
const noNotchDevicePadding = bottomPadding || 10;
|
||||
const iosVerticalOffset
|
||||
= deviceHeight + noNotchDevicePadding + tabNavigatorPadding;
|
||||
= headerHeight + noNotchDevicePadding + tabNavigatorPadding;
|
||||
const androidVerticalOffset = hasBottomTextInput
|
||||
? deviceHeight + StatusBar.currentHeight : deviceHeight;
|
||||
? headerHeight + StatusBar.currentHeight : headerHeight;
|
||||
|
||||
// Tells the view what to do with taps
|
||||
const shouldSetResponse = useCallback(() => true);
|
||||
|
||||
@@ -37,11 +37,6 @@ type Props = {
|
||||
*/
|
||||
hasTabNavigator?: boolean,
|
||||
|
||||
/**
|
||||
* Is the screen presented as a modal?
|
||||
*/
|
||||
isModalPresentation?: boolean,
|
||||
|
||||
/**
|
||||
* Insets for the SafeAreaView.
|
||||
*/
|
||||
@@ -59,7 +54,6 @@ const JitsiScreen = ({
|
||||
footerComponent,
|
||||
hasTabNavigator = false,
|
||||
hasBottomTextInput = false,
|
||||
isModalPresentation = true,
|
||||
safeAreaInsets = [ 'left', 'right' ],
|
||||
style
|
||||
}: Props) => (
|
||||
@@ -69,7 +63,6 @@ const JitsiScreen = ({
|
||||
contentContainerStyle = { contentContainerStyle }
|
||||
hasBottomTextInput = { hasBottomTextInput }
|
||||
hasTabNavigator = { hasTabNavigator }
|
||||
isModalPresentation = { isModalPresentation }
|
||||
style = { style }>
|
||||
<SafeAreaView
|
||||
edges = { safeAreaInsets }
|
||||
|
||||
@@ -236,7 +236,8 @@ export const OVERWRITE_PARTICIPANTS_NAMES = 'OVERWRITE_PARTICIPANTS_NAMES';
|
||||
* Updates participants local recording status.
|
||||
* {
|
||||
* type: SET_LOCAL_PARTICIPANT_RECORDING_STATUS,
|
||||
* recording: boolean
|
||||
* recording: boolean,
|
||||
* onlySelf: boolean
|
||||
* }
|
||||
*/
|
||||
export const SET_LOCAL_PARTICIPANT_RECORDING_STATUS = 'SET_LOCAL_PARTICIPANT_RECORDING_STATUS';
|
||||
|
||||
@@ -378,6 +378,7 @@ export function hiddenParticipantLeft(id) {
|
||||
* participant is allowed to not specify an associated {@code JitsiConference}
|
||||
* instance.
|
||||
* @param {boolean} isReplaced - Whether the participant is to be replaced in the meeting.
|
||||
* @param {boolean} isVirtualScreenshareParticipant - Whether the participant is a virtual screen share participant.
|
||||
* @returns {{
|
||||
* type: PARTICIPANT_LEFT,
|
||||
* participant: {
|
||||
@@ -386,13 +387,14 @@ export function hiddenParticipantLeft(id) {
|
||||
* }
|
||||
* }}
|
||||
*/
|
||||
export function participantLeft(id, conference, isReplaced) {
|
||||
export function participantLeft(id, conference, isReplaced, isVirtualScreenshareParticipant) {
|
||||
return {
|
||||
type: PARTICIPANT_LEFT,
|
||||
participant: {
|
||||
conference,
|
||||
id,
|
||||
isReplaced
|
||||
isReplaced,
|
||||
isVirtualScreenshareParticipant
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -689,14 +691,16 @@ export function overwriteParticipantsNames(participantList) {
|
||||
* Local video recording status for the local participant.
|
||||
*
|
||||
* @param {boolean} recording - If local recording is ongoing.
|
||||
* @param {boolean} onlySelf - If recording only local streams.
|
||||
* @returns {{
|
||||
* type: SET_LOCAL_PARTICIPANT_RECORDING_STATUS,
|
||||
* recording: boolean
|
||||
* }}
|
||||
*/
|
||||
export function updateLocalRecordingStatus(recording) {
|
||||
export function updateLocalRecordingStatus(recording, onlySelf) {
|
||||
return {
|
||||
type: SET_LOCAL_PARTICIPANT_RECORDING_STATUS,
|
||||
recording
|
||||
recording,
|
||||
onlySelf
|
||||
};
|
||||
}
|
||||
|
||||
@@ -179,11 +179,11 @@ MiddlewareRegistry.register(store => next => action => {
|
||||
|
||||
case SET_LOCAL_PARTICIPANT_RECORDING_STATUS: {
|
||||
const state = store.getState();
|
||||
const { recording } = action;
|
||||
const { recording, onlySelf } = action;
|
||||
const localId = getLocalParticipant(state)?.id;
|
||||
const { localRecording } = state['features/base/config'];
|
||||
|
||||
if (localRecording.notifyAllParticipants) {
|
||||
if (localRecording?.notifyAllParticipants && !onlySelf) {
|
||||
store.dispatch(participantUpdated({
|
||||
// XXX Only the local participant is allowed to update without
|
||||
// stating the JitsiConference instance (i.e. participant property
|
||||
|
||||
@@ -9,7 +9,7 @@ import { isIconUrl } from './functions';
|
||||
* @param {string | Object} src - Source of the avatar.
|
||||
* @param {boolean} useCORS - Whether to use CORS or not.
|
||||
* @param {boolean} tryOnce - If true we try to load the image only using the specified CORS mode. Otherwise both modes
|
||||
* (CORS and no CORS) will be used to load the image if the first atempt fails.
|
||||
* (CORS and no CORS) will be used to load the image if the first attempt fails.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function preloadImage(
|
||||
|
||||
@@ -445,7 +445,7 @@ function _getDisplayName(state: Object, name: string): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops trough the participants in the state in order to check if all participants are moderators.
|
||||
* Loops through the participants in the state in order to check if all participants are moderators.
|
||||
*
|
||||
* @param {Object} state - The local participant redux state.
|
||||
* @returns {boolean}
|
||||
|
||||
@@ -52,14 +52,14 @@ function _updateScreenshareParticipants({ getState, dispatch }) {
|
||||
}
|
||||
|
||||
if (localScreenShare && !newLocalSceenshareSourceName) {
|
||||
dispatch(participantLeft(localScreenShare.id, conference));
|
||||
dispatch(participantLeft(localScreenShare.id, conference, undefined, true));
|
||||
}
|
||||
|
||||
const removedScreenshareSourceNames = _.difference(previousScreenshareSourceNames, currentScreenshareSourceNames);
|
||||
const addedScreenshareSourceNames = _.difference(currentScreenshareSourceNames, previousScreenshareSourceNames);
|
||||
|
||||
if (removedScreenshareSourceNames.length) {
|
||||
removedScreenshareSourceNames.forEach(id => dispatch(participantLeft(id, conference)));
|
||||
removedScreenshareSourceNames.forEach(id => dispatch(participantLeft(id, conference, undefined, true)));
|
||||
}
|
||||
|
||||
if (addedScreenshareSourceNames.length) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user