mirror of
https://gitcode.com/GitHub_Trending/ji/jitsi-meet.git
synced 2026-05-23 19:47:46 +00:00
Compare commits
13 Commits
3204
...
remove-add
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a56eb83346 | ||
|
|
b7133f5717 | ||
|
|
f77e1dc591 | ||
|
|
4d817fc6c2 | ||
|
|
b8a7037959 | ||
|
|
6f95c50d6e | ||
|
|
9f3ef43daa | ||
|
|
46713cab3b | ||
|
|
8065cc0348 | ||
|
|
045a2d6aca | ||
|
|
d7d9bc4eeb | ||
|
|
33db155eb9 | ||
|
|
1cfd6164f5 |
@@ -24,10 +24,6 @@ import android.util.Log;
|
||||
import org.jitsi.meet.sdk.JitsiMeetActivity;
|
||||
import org.jitsi.meet.sdk.JitsiMeetView;
|
||||
import org.jitsi.meet.sdk.JitsiMeetViewListener;
|
||||
import org.jitsi.meet.sdk.invite.AddPeopleController;
|
||||
import org.jitsi.meet.sdk.invite.AddPeopleControllerListener;
|
||||
import org.jitsi.meet.sdk.invite.InviteController;
|
||||
import org.jitsi.meet.sdk.invite.InviteControllerListener;
|
||||
|
||||
import com.crashlytics.android.Crashlytics;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
@@ -36,8 +32,6 @@ import io.fabric.sdk.android.Fabric;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -53,13 +47,6 @@ import java.util.Map;
|
||||
* {@code react-native run-android}.
|
||||
*/
|
||||
public class MainActivity extends JitsiMeetActivity {
|
||||
/**
|
||||
* The query to perform through {@link AddPeopleController} when the
|
||||
* {@code InviteButton} is tapped in order to exercise the public API of the
|
||||
* feature invite. If {@code null}, the {@code InviteButton} will not be
|
||||
* rendered.
|
||||
*/
|
||||
private static final String ADD_PEOPLE_CONTROLLER_QUERY = null;
|
||||
|
||||
@Override
|
||||
protected JitsiMeetView initializeView() {
|
||||
@@ -113,72 +100,11 @@ public class MainActivity extends JitsiMeetActivity {
|
||||
}
|
||||
});
|
||||
|
||||
// inviteController
|
||||
final InviteController inviteController
|
||||
= view.getInviteController();
|
||||
|
||||
inviteController.setListener(new InviteControllerListener() {
|
||||
public void beginAddPeople(
|
||||
AddPeopleController addPeopleController) {
|
||||
onInviteControllerBeginAddPeople(
|
||||
inviteController,
|
||||
addPeopleController);
|
||||
}
|
||||
});
|
||||
inviteController.setAddPeopleEnabled(
|
||||
ADD_PEOPLE_CONTROLLER_QUERY != null);
|
||||
inviteController.setDialOutEnabled(
|
||||
inviteController.isAddPeopleEnabled());
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
private void onAddPeopleControllerInviteSettled(
|
||||
AddPeopleController addPeopleController,
|
||||
List<Map<String, Object>> failedInvitees) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
|
||||
// XXX Explicitly invoke endAddPeople on addPeopleController; otherwise,
|
||||
// it is going to be memory-leaked in the associated InviteController
|
||||
// and no subsequent InviteButton clicks/taps will be delivered.
|
||||
// Technically, endAddPeople will automatically be invoked if there are
|
||||
// no failedInviteees i.e. the invite succeeeded for all specified
|
||||
// invitees.
|
||||
addPeopleController.endAddPeople();
|
||||
}
|
||||
|
||||
private void onAddPeopleControllerReceivedResults(
|
||||
AddPeopleController addPeopleController,
|
||||
List<Map<String, Object>> results,
|
||||
String query) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
|
||||
int size = results.size();
|
||||
|
||||
if (size > 0) {
|
||||
// Exercise AddPeopleController's inviteById implementation.
|
||||
List<String> ids = new ArrayList<>(size);
|
||||
|
||||
for (Map<String, Object> result : results) {
|
||||
Object id = result.get("id");
|
||||
|
||||
if (id != null) {
|
||||
ids.add(id.toString());
|
||||
}
|
||||
}
|
||||
|
||||
addPeopleController.inviteById(ids);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// XXX Explicitly invoke endAddPeople on addPeopleController; otherwise,
|
||||
// it is going to be memory-leaked in the associated InviteController
|
||||
// and no subsequent InviteButton clicks/taps will be delivered.
|
||||
addPeopleController.endAddPeople();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
// As this is the Jitsi Meet app (i.e. not the Jitsi Meet SDK), we do
|
||||
@@ -213,47 +139,4 @@ public class MainActivity extends JitsiMeetActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private void onInviteControllerBeginAddPeople(
|
||||
InviteController inviteController,
|
||||
AddPeopleController addPeopleController) {
|
||||
UiThreadUtil.assertOnUiThread();
|
||||
|
||||
// Log with the tag "ReactNative" in order to have the log visible in
|
||||
// react-native log-android as well.
|
||||
Log.d(
|
||||
"ReactNative",
|
||||
InviteControllerListener.class.getSimpleName() + ".beginAddPeople");
|
||||
|
||||
String query = ADD_PEOPLE_CONTROLLER_QUERY;
|
||||
|
||||
if (query != null
|
||||
&& (inviteController.isAddPeopleEnabled()
|
||||
|| inviteController.isDialOutEnabled())) {
|
||||
addPeopleController.setListener(new AddPeopleControllerListener() {
|
||||
public void onInviteSettled(
|
||||
AddPeopleController addPeopleController,
|
||||
List<Map<String, Object>> failedInvitees) {
|
||||
onAddPeopleControllerInviteSettled(
|
||||
addPeopleController,
|
||||
failedInvitees);
|
||||
}
|
||||
|
||||
public void onReceivedResults(
|
||||
AddPeopleController addPeopleController,
|
||||
List<Map<String, Object>> results,
|
||||
String query) {
|
||||
onAddPeopleControllerReceivedResults(
|
||||
addPeopleController,
|
||||
results, query);
|
||||
}
|
||||
});
|
||||
addPeopleController.performQuery(query);
|
||||
} else {
|
||||
// XXX Explicitly invoke endAddPeople on addPeopleController;
|
||||
// otherwise, it is going to be memory-leaked in the associated
|
||||
// InviteController and no subsequent InviteButton clicks/taps will
|
||||
// be delivered.
|
||||
addPeopleController.endAddPeople();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,8 +24,6 @@ import android.util.Log;
|
||||
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
|
||||
import org.jitsi.meet.sdk.invite.InviteController;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.URL;
|
||||
import java.util.Map;
|
||||
@@ -78,12 +76,6 @@ public class JitsiMeetView
|
||||
*/
|
||||
private URL defaultURL;
|
||||
|
||||
/**
|
||||
* The entry point into the invite feature of Jitsi Meet. The Java
|
||||
* counterpart of the JavaScript {@code InviteButton}.
|
||||
*/
|
||||
private final InviteController inviteController;
|
||||
|
||||
/**
|
||||
* Whether Picture-in-Picture is enabled. If {@code null}, defaults to
|
||||
* {@code true} iff the Android platform supports Picture-in-Picture
|
||||
@@ -106,10 +98,6 @@ public class JitsiMeetView
|
||||
public JitsiMeetView(@NonNull Context context) {
|
||||
super(context);
|
||||
|
||||
// The entry point into the invite feature of Jitsi Meet. The Java
|
||||
// counterpart of the JavaScript InviteButton.
|
||||
inviteController = new InviteController(externalAPIScope);
|
||||
|
||||
// Check if the parent Activity implements JitsiMeetActivityInterface,
|
||||
// otherwise things may go wrong.
|
||||
if (!(context instanceof JitsiMeetActivityInterface)) {
|
||||
@@ -155,19 +143,6 @@ public class JitsiMeetView
|
||||
return defaultURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link InviteController} which represents the entry point into
|
||||
* the invite feature of Jitsi Meet and is the Java counterpart of the
|
||||
* JavaScript {@code InviteButton}.
|
||||
*
|
||||
* @return the {@link InviteController} which represents the entry point
|
||||
* into the invite feature of Jitsi Meet and is the Java counterpart of the
|
||||
* JavaScript {@code InviteButton}
|
||||
*/
|
||||
public InviteController getInviteController() {
|
||||
return inviteController;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the URL of the current conference.
|
||||
*
|
||||
@@ -238,18 +213,6 @@ public class JitsiMeetView
|
||||
props.putString("defaultURL", defaultURL.toString());
|
||||
}
|
||||
|
||||
// inviteController
|
||||
InviteController inviteController = getInviteController();
|
||||
|
||||
if (inviteController != null) {
|
||||
props.putBoolean(
|
||||
"addPeopleEnabled",
|
||||
inviteController.isAddPeopleEnabled());
|
||||
props.putBoolean(
|
||||
"dialOutEnabled",
|
||||
inviteController.isDialOutEnabled());
|
||||
}
|
||||
|
||||
// pictureInPictureEnabled
|
||||
props.putBoolean(
|
||||
"pictureInPictureEnabled",
|
||||
|
||||
@@ -54,7 +54,6 @@ class ReactInstanceManagerHolder {
|
||||
new ProximityModule(reactContext),
|
||||
new WiFiStatsModule(reactContext),
|
||||
new org.jitsi.meet.sdk.dropbox.Dropbox(reactContext),
|
||||
new org.jitsi.meet.sdk.invite.InviteModule(reactContext),
|
||||
new org.jitsi.meet.sdk.net.NAT64AddrInfoModule(reactContext)));
|
||||
|
||||
if (android.os.Build.VERSION.SDK_INT
|
||||
|
||||
@@ -1,211 +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.invite;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.ReadableMap;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableNativeArray;
|
||||
import com.facebook.react.bridge.WritableNativeMap;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Controller object used by native code to query and submit user selections for
|
||||
* the user invitation flow.
|
||||
*/
|
||||
public class AddPeopleController {
|
||||
|
||||
/**
|
||||
* The AddPeopleControllerListener for this controller, used to pass query
|
||||
* results back to the native code that initiated the query.
|
||||
*/
|
||||
private AddPeopleControllerListener listener;
|
||||
|
||||
/**
|
||||
* Local cache of search query results. Used to re-hydrate the list of
|
||||
* selected items based on their ids passed to inviteById in order to pass
|
||||
* the full item maps back to the JitsiMeetView during submission.
|
||||
*/
|
||||
private final Map<String, ReadableMap> items = new HashMap<>();
|
||||
|
||||
private final WeakReference<InviteController> owner;
|
||||
|
||||
private final WeakReference<ReactApplicationContext> reactContext;
|
||||
|
||||
/**
|
||||
* Randomly generated UUID, used for identification in the InviteModule.
|
||||
*/
|
||||
private final String uuid = UUID.randomUUID().toString();
|
||||
|
||||
public AddPeopleController(
|
||||
InviteController owner,
|
||||
ReactApplicationContext reactContext) {
|
||||
this.owner = new WeakReference<>(owner);
|
||||
this.reactContext = new WeakReference<>(reactContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel the invitation flow and free memory allocated to the
|
||||
* AddPeopleController. After calling this method, this object is invalid -
|
||||
* a new AddPeopleController will be passed to the caller through
|
||||
* beginAddPeople.
|
||||
*/
|
||||
public void endAddPeople() {
|
||||
InviteController owner = this.owner.get();
|
||||
|
||||
if (owner != null) {
|
||||
owner.endAddPeople(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the AddPeopleControllerListener for this controller, used to pass
|
||||
* query results back to the native code that initiated the query.
|
||||
*/
|
||||
public AddPeopleControllerListener getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
final ReactApplicationContext getReactApplicationContext() {
|
||||
return reactContext.get();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @return the unique identifier for this AddPeopleController
|
||||
*/
|
||||
public String getUuid() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send invites to selected users based on their item ids
|
||||
*
|
||||
* @param ids
|
||||
*/
|
||||
public void inviteById(List<String> ids) {
|
||||
InviteController owner = this.owner.get();
|
||||
|
||||
if (owner != null) {
|
||||
WritableArray invitees = new WritableNativeArray();
|
||||
|
||||
for(int i = 0, size = ids.size(); i < size; i++) {
|
||||
String id = ids.get(i);
|
||||
|
||||
if(items.containsKey(id)) {
|
||||
WritableNativeMap map = new WritableNativeMap();
|
||||
map.merge(items.get(id));
|
||||
invitees.pushMap(map);
|
||||
} else {
|
||||
// If the id doesn't exist in the map, we can't do anything,
|
||||
// so just skip it.
|
||||
}
|
||||
}
|
||||
|
||||
owner.invite(this, invitees);
|
||||
}
|
||||
}
|
||||
|
||||
void inviteSettled(ReadableArray failedInvitees) {
|
||||
AddPeopleControllerListener listener = getListener();
|
||||
|
||||
if (listener != null) {
|
||||
ArrayList<Map<String, Object>> jFailedInvitees = new ArrayList<>();
|
||||
|
||||
for (int i = 0, size = failedInvitees.size(); i < size; ++i) {
|
||||
jFailedInvitees.add(failedInvitees.getMap(i).toHashMap());
|
||||
}
|
||||
|
||||
listener.onInviteSettled(this, jFailedInvitees);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a search for entities to invite with the given query. Results will
|
||||
* be returned through the associated AddPeopleControllerListener's
|
||||
* onReceivedResults method.
|
||||
*
|
||||
* @param query
|
||||
*/
|
||||
public void performQuery(String query) {
|
||||
InviteController owner = this.owner.get();
|
||||
|
||||
if (owner != null) {
|
||||
owner.performQuery(this, query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Caches results received by the search into a local map for use later when
|
||||
* the items are submitted. Submission requires the full map of
|
||||
* information, but only the IDs are returned back to the delegate. Using
|
||||
* this map means we don't have to send the whole map back to the delegate.
|
||||
*
|
||||
* @param results
|
||||
* @param query
|
||||
*/
|
||||
void receivedResultsForQuery(ReadableArray results, String query) {
|
||||
AddPeopleControllerListener listener = getListener();
|
||||
|
||||
if (listener != null) {
|
||||
List<Map<String, Object>> jvmResults = new ArrayList<>();
|
||||
|
||||
// cache results for use in submission later
|
||||
// convert to jvm array
|
||||
for(int i = 0; i < results.size(); i++) {
|
||||
ReadableMap map = results.getMap(i);
|
||||
|
||||
if(map.hasKey("id")) {
|
||||
items.put(map.getString("id"), map);
|
||||
} else if(map.hasKey("type")
|
||||
&& map.getString("type").equals("phone")
|
||||
&& map.hasKey("number")) {
|
||||
items.put(map.getString("number"), map);
|
||||
} else {
|
||||
Log.w(
|
||||
"AddPeopleController",
|
||||
"Received result without id and that was not a phone number, so not adding it to suggestions: "
|
||||
+ map);
|
||||
}
|
||||
|
||||
jvmResults.add(map.toHashMap());
|
||||
}
|
||||
|
||||
listener.onReceivedResults(this, jvmResults, query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the AddPeopleControllerListener for this controller, used to pass
|
||||
* query results back to the native code that initiated the query.
|
||||
*
|
||||
* @param listener
|
||||
*/
|
||||
public void setListener(AddPeopleControllerListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +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.invite;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface AddPeopleControllerListener {
|
||||
/**
|
||||
* Called when the call to {@link AddPeopleController#inviteById(List)}
|
||||
* completes.
|
||||
*
|
||||
* @param addPeopleController the active {@link AddPeopleController} for
|
||||
* this invite flow. This object should be cleaned up by calling
|
||||
* {@link AddPeopleController#endAddPeople()} if the user exits the invite
|
||||
* flow. Otherwise, it can stay active if the user will attempt to invite
|
||||
* @param failedInvitees a {@code List} of {@code Map<String, Object>}
|
||||
* dictionaries that represent the invitations that failed. The data type of
|
||||
* the objects is identical to the results returned in onReceivedResuls.
|
||||
*/
|
||||
void onInviteSettled(
|
||||
AddPeopleController addPeopleController,
|
||||
List<Map<String, Object>> failedInvitees);
|
||||
|
||||
/**
|
||||
* Called when results are received for a query called through
|
||||
* AddPeopleController.query().
|
||||
*
|
||||
* @param addPeopleController
|
||||
* @param results a List of Map<String, Object> objects that represent items
|
||||
* returned by the query. The object at key "type" describes the type of
|
||||
* item: "user", "videosipgw" (conference room), or "phone". "user" types
|
||||
* have properties at "id", "name", and "avatar". "videosipgw" types have
|
||||
* properties at "id" and "name". "phone" types have properties at "number",
|
||||
* "title", "and "subtitle"
|
||||
* @param query the query that generated the given results
|
||||
*/
|
||||
void onReceivedResults(
|
||||
AddPeopleController addPeopleController,
|
||||
List<Map<String, Object>> results,
|
||||
String query);
|
||||
}
|
||||
@@ -1,265 +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.invite;
|
||||
|
||||
import com.facebook.react.bridge.Arguments;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.WritableArray;
|
||||
import com.facebook.react.bridge.WritableNativeMap;
|
||||
|
||||
import org.jitsi.meet.sdk.ReactContextUtils;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.FutureTask;
|
||||
|
||||
/**
|
||||
* Represents the entry point into the invite feature of Jitsi Meet and is the
|
||||
* Java counterpart of the JavaScript {@code InviteButton}.
|
||||
*/
|
||||
public class InviteController {
|
||||
private AddPeopleController addPeopleController;
|
||||
|
||||
/**
|
||||
* Whether adding/inviting people by name (as opposed to phone number) is
|
||||
* enabled.
|
||||
*/
|
||||
private Boolean addPeopleEnabled;
|
||||
|
||||
/**
|
||||
* Whether adding/inviting people by phone number (as opposed to name) is
|
||||
* enabled.
|
||||
*/
|
||||
private Boolean dialOutEnabled;
|
||||
|
||||
private final String externalAPIScope;
|
||||
|
||||
private InviteControllerListener listener;
|
||||
|
||||
public InviteController(String externalAPIScope) {
|
||||
this.externalAPIScope = externalAPIScope;
|
||||
}
|
||||
|
||||
void beginAddPeople(ReactApplicationContext reactContext) {
|
||||
InviteControllerListener listener = getListener();
|
||||
|
||||
if (listener != null) {
|
||||
// XXX For the sake of simplicity and in order to reduce the risk of
|
||||
// memory leaks, allow a single AddPeopleController at a time.
|
||||
AddPeopleController addPeopleController = this.addPeopleController;
|
||||
|
||||
if (addPeopleController != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize a new AddPeopleController to represent the click/tap
|
||||
// on the InviteButton and notify the InviteControllerListener
|
||||
// about the event.
|
||||
addPeopleController = new AddPeopleController(this, reactContext);
|
||||
|
||||
boolean success = false;
|
||||
|
||||
this.addPeopleController = addPeopleController;
|
||||
try {
|
||||
listener.beginAddPeople(addPeopleController);
|
||||
success = true;
|
||||
} finally {
|
||||
if (!success) {
|
||||
endAddPeople(addPeopleController);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void endAddPeople(AddPeopleController addPeopleController) {
|
||||
if (this.addPeopleController == addPeopleController) {
|
||||
this.addPeopleController = null;
|
||||
}
|
||||
}
|
||||
|
||||
public InviteControllerListener getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends JavaScript event to submit invitations to the given item ids
|
||||
*
|
||||
* @param invitees a WritableArray of WritableNativeMaps representing
|
||||
* selected items. Each map representing a selected item should match the
|
||||
* data passed back in the return from a query.
|
||||
*/
|
||||
boolean invite(
|
||||
AddPeopleController addPeopleController,
|
||||
WritableArray invitees) {
|
||||
return
|
||||
invite(
|
||||
addPeopleController.getUuid(),
|
||||
addPeopleController.getReactApplicationContext(),
|
||||
invitees);
|
||||
}
|
||||
|
||||
public Future<List<Map<String, Object>>> invite(
|
||||
final List<Map<String, Object>> invitees) {
|
||||
final boolean inviteBegan
|
||||
= invite(
|
||||
UUID.randomUUID().toString(),
|
||||
/* reactContext */ null,
|
||||
Arguments.makeNativeArray(invitees));
|
||||
FutureTask futureTask
|
||||
= new FutureTask(new Callable() {
|
||||
@Override
|
||||
public List<Map<String, Object>> call() {
|
||||
if (inviteBegan) {
|
||||
// TODO Complete the returned Future when the invite
|
||||
// settles.
|
||||
return Collections.emptyList();
|
||||
} else {
|
||||
// The invite failed to even begin so report that all
|
||||
// invitees failed.
|
||||
return invitees;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// If the invite failed to even begin, complete the returned Future
|
||||
// already and the Future implementation will report that all invitees
|
||||
// failed.
|
||||
if (!inviteBegan) {
|
||||
futureTask.run();
|
||||
}
|
||||
|
||||
return futureTask;
|
||||
}
|
||||
|
||||
private boolean invite(
|
||||
String addPeopleControllerScope,
|
||||
ReactContext reactContext,
|
||||
WritableArray invitees) {
|
||||
WritableNativeMap data = new WritableNativeMap();
|
||||
|
||||
data.putString("addPeopleControllerScope", addPeopleControllerScope);
|
||||
data.putString("externalAPIScope", externalAPIScope);
|
||||
data.putArray("invitees", invitees);
|
||||
|
||||
return
|
||||
ReactContextUtils.emitEvent(
|
||||
reactContext,
|
||||
"org.jitsi.meet:features/invite#invite",
|
||||
data);
|
||||
}
|
||||
|
||||
void inviteSettled(
|
||||
String addPeopleControllerScope,
|
||||
ReadableArray failedInvitees) {
|
||||
AddPeopleController addPeopleController = this.addPeopleController;
|
||||
|
||||
if (addPeopleController != null
|
||||
&& addPeopleController.getUuid().equals(
|
||||
addPeopleControllerScope)) {
|
||||
try {
|
||||
addPeopleController.inviteSettled(failedInvitees);
|
||||
} finally {
|
||||
if (failedInvitees.size() == 0) {
|
||||
endAddPeople(addPeopleController);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAddPeopleEnabled() {
|
||||
Boolean b = this.addPeopleEnabled;
|
||||
|
||||
return
|
||||
(b == null || b.booleanValue()) ? (getListener() != null) : false;
|
||||
}
|
||||
|
||||
public boolean isDialOutEnabled() {
|
||||
Boolean b = this.dialOutEnabled;
|
||||
|
||||
return
|
||||
(b == null || b.booleanValue()) ? (getListener() != null) : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts a query for users to invite to the conference. Results will be
|
||||
* returned through
|
||||
* {@link AddPeopleControllerListener#onReceivedResults(AddPeopleController, List, String)}.
|
||||
*
|
||||
* @param query {@code String} to use for the query
|
||||
*/
|
||||
void performQuery(AddPeopleController addPeopleController, String query) {
|
||||
WritableNativeMap params = new WritableNativeMap();
|
||||
|
||||
params.putString("addPeopleControllerScope", addPeopleController.getUuid());
|
||||
params.putString("externalAPIScope", externalAPIScope);
|
||||
params.putString("query", query);
|
||||
ReactContextUtils.emitEvent(
|
||||
addPeopleController.getReactApplicationContext(),
|
||||
"org.jitsi.meet:features/invite#performQuery",
|
||||
params);
|
||||
}
|
||||
|
||||
void receivedResultsForQuery(
|
||||
String addPeopleControllerScope,
|
||||
String query,
|
||||
ReadableArray results) {
|
||||
AddPeopleController addPeopleController = this.addPeopleController;
|
||||
|
||||
if (addPeopleController != null
|
||||
&& addPeopleController.getUuid().equals(
|
||||
addPeopleControllerScope)) {
|
||||
addPeopleController.receivedResultsForQuery(results, query);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the ability to add users to the call is enabled. If this is
|
||||
* enabled, an add user button will appear on the {@link JitsiMeetView}. If
|
||||
* enabled, and the user taps the add user button,
|
||||
* {@link InviteControllerListener#beginAddPeople(AddPeopleController)}
|
||||
* will be called.
|
||||
*
|
||||
* @param addPeopleEnabled {@code true} to enable the add people button;
|
||||
* otherwise, {@code false}
|
||||
*/
|
||||
public void setAddPeopleEnabled(boolean addPeopleEnabled) {
|
||||
this.addPeopleEnabled = Boolean.valueOf(addPeopleEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the ability to add phone numbers to the call is enabled.
|
||||
* Must be enabled along with {@link #setAddPeopleEnabled(boolean)} to be
|
||||
* effective.
|
||||
*
|
||||
* @param dialOutEnabled {@code true} to enable the ability to add phone
|
||||
* numbers to the call; otherwise, {@code false}
|
||||
*/
|
||||
public void setDialOutEnabled(boolean dialOutEnabled) {
|
||||
this.dialOutEnabled = Boolean.valueOf(dialOutEnabled);
|
||||
}
|
||||
|
||||
public void setListener(InviteControllerListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
}
|
||||
@@ -1,29 +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.invite;
|
||||
|
||||
public interface InviteControllerListener {
|
||||
/**
|
||||
* Called when the add user button is tapped.
|
||||
*
|
||||
* @param addPeopleController {@code AddPeopleController} scoped for this
|
||||
* user invite flow. The {@code AddPeopleController} is used to start user
|
||||
* queries and accepts an {@code AddPeopleControllerListener} for receiving
|
||||
* user query responses.
|
||||
*/
|
||||
void beginAddPeople(AddPeopleController addPeopleController);
|
||||
}
|
||||
@@ -1,171 +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.invite;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContextBaseJavaModule;
|
||||
import com.facebook.react.bridge.ReactMethod;
|
||||
import com.facebook.react.bridge.ReadableArray;
|
||||
import com.facebook.react.bridge.UiThreadUtil;
|
||||
|
||||
import org.jitsi.meet.sdk.BaseReactView;
|
||||
import org.jitsi.meet.sdk.JitsiMeetView;
|
||||
|
||||
/**
|
||||
* Implements the react-native module of the feature invite.
|
||||
*/
|
||||
public class InviteModule
|
||||
extends ReactContextBaseJavaModule {
|
||||
|
||||
public InviteModule(ReactApplicationContext reactContext) {
|
||||
super(reactContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals that a click/tap has been performed on {@code InviteButton} and
|
||||
* that the execution flow for adding/inviting people to the current
|
||||
* conference/meeting is to begin
|
||||
*
|
||||
* @param externalAPIScope the unique identifier of the
|
||||
* {@code JitsiMeetView} whose {@code InviteButton} was clicked/tapped.
|
||||
*/
|
||||
@ReactMethod
|
||||
public void beginAddPeople(final String externalAPIScope) {
|
||||
// Make sure InviteControllerListener (like all other listeners of the
|
||||
// SDK) is invoked on the UI thread. It was requested by SDK consumers.
|
||||
if (!UiThreadUtil.isOnUiThread()) {
|
||||
UiThreadUtil.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
beginAddPeople(externalAPIScope);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
InviteController inviteController
|
||||
= findInviteControllerByExternalAPIScope(externalAPIScope);
|
||||
|
||||
if (inviteController != null) {
|
||||
inviteController.beginAddPeople(getReactApplicationContext());
|
||||
}
|
||||
}
|
||||
|
||||
private InviteController findInviteControllerByExternalAPIScope(
|
||||
String externalAPIScope) {
|
||||
JitsiMeetView view
|
||||
= (JitsiMeetView)
|
||||
BaseReactView.findViewByExternalAPIScope(externalAPIScope);
|
||||
|
||||
return view == null ? null : view.getInviteController();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Invite";
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for invitation failures
|
||||
*
|
||||
* @param failedInvitees the items for which the invitation failed
|
||||
* @param addPeopleControllerScope a string that represents a connection to
|
||||
* a specific AddPeopleController
|
||||
*/
|
||||
@ReactMethod
|
||||
public void inviteSettled(
|
||||
final String externalAPIScope,
|
||||
final String addPeopleControllerScope,
|
||||
final ReadableArray failedInvitees) {
|
||||
// Make sure AddPeopleControllerListener (like all other listeners of
|
||||
// the SDK) is invoked on the UI thread. It was requested by SDK
|
||||
// consumers.
|
||||
if (!UiThreadUtil.isOnUiThread()) {
|
||||
UiThreadUtil.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
inviteSettled(
|
||||
externalAPIScope,
|
||||
addPeopleControllerScope,
|
||||
failedInvitees);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
InviteController inviteController
|
||||
= findInviteControllerByExternalAPIScope(externalAPIScope);
|
||||
|
||||
if (inviteController == null) {
|
||||
Log.w(
|
||||
"InviteModule",
|
||||
"Invite settled, but failed to find active controller to notify");
|
||||
} else {
|
||||
inviteController.inviteSettled(
|
||||
addPeopleControllerScope,
|
||||
failedInvitees);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for results received from the JavaScript invite search call
|
||||
*
|
||||
* @param results the results in a ReadableArray of ReadableMap objects
|
||||
* @param query the query associated with the search
|
||||
* @param addPeopleControllerScope a string that represents a connection to
|
||||
* a specific AddPeopleController
|
||||
*/
|
||||
@ReactMethod
|
||||
public void receivedResults(
|
||||
final String externalAPIScope,
|
||||
final String addPeopleControllerScope,
|
||||
final String query,
|
||||
final ReadableArray results) {
|
||||
// Make sure AddPeopleControllerListener (like all other listeners of
|
||||
// the SDK) is invoked on the UI thread. It was requested by SDK
|
||||
// consumers.
|
||||
if (!UiThreadUtil.isOnUiThread()) {
|
||||
UiThreadUtil.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
receivedResults(
|
||||
externalAPIScope,
|
||||
addPeopleControllerScope,
|
||||
query,
|
||||
results);
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
InviteController inviteController
|
||||
= findInviteControllerByExternalAPIScope(externalAPIScope);
|
||||
|
||||
if (inviteController == null) {
|
||||
Log.w(
|
||||
"InviteModule",
|
||||
"Received results, but failed to find active controller to send results back");
|
||||
} else {
|
||||
inviteController.receivedResultsForQuery(
|
||||
addPeopleControllerScope,
|
||||
query,
|
||||
results);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1237,6 +1237,7 @@ export default {
|
||||
= connection.initJitsiConference(
|
||||
APP.conference.roomName,
|
||||
this._getConferenceOptions());
|
||||
|
||||
APP.store.dispatch(conferenceWillJoin(room));
|
||||
this._setLocalAudioVideoStreams(localTracks);
|
||||
this._room = room; // FIXME do not use this
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
background: #0074E0;
|
||||
border-radius: 4px;
|
||||
color: #FFFFFF;
|
||||
display: inline-block;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 5px 10px;
|
||||
|
||||
@@ -496,18 +496,22 @@
|
||||
display:none !important;
|
||||
}
|
||||
|
||||
#dominantSpeakerAvatarContainer,
|
||||
#dominantSpeakerAvatar,
|
||||
.dynamic-shadow {
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
#dominantSpeakerAvatar {
|
||||
#dominantSpeakerAvatarContainer {
|
||||
top: 50px;
|
||||
margin: auto;
|
||||
position: relative;
|
||||
border-radius: 100px;
|
||||
overflow: hidden;
|
||||
visibility: inherit;
|
||||
}
|
||||
#dominantSpeakerAvatar {
|
||||
background-color: #000000;
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@ target 'JitsiMeet' do
|
||||
:path => '../node_modules/react-native-fast-image'
|
||||
pod 'react-native-keep-awake',
|
||||
:path => '../node_modules/react-native-keep-awake'
|
||||
pod 'BVLinearGradient',
|
||||
:path => '../node_modules/react-native-linear-gradient'
|
||||
pod 'react-native-webrtc', :path => '../node_modules/react-native-webrtc'
|
||||
pod 'RNGoogleSignin',
|
||||
:path => '../node_modules/react-native-google-signin'
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
PODS:
|
||||
- boost-for-react-native (1.63.0)
|
||||
- BVLinearGradient (2.5.3):
|
||||
- React
|
||||
- Crashlytics (3.12.0):
|
||||
- Fabric (~> 1.9.0)
|
||||
- DoubleConversion (1.1.6)
|
||||
@@ -152,6 +154,7 @@ PODS:
|
||||
- yoga (0.57.8.React)
|
||||
|
||||
DEPENDENCIES:
|
||||
- BVLinearGradient (from `../node_modules/react-native-linear-gradient`)
|
||||
- Crashlytics
|
||||
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
|
||||
- Fabric
|
||||
@@ -202,6 +205,8 @@ SPEC REPOS:
|
||||
- SDWebImage
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
BVLinearGradient:
|
||||
:path: "../node_modules/react-native-linear-gradient"
|
||||
DoubleConversion:
|
||||
:podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec"
|
||||
Folly:
|
||||
@@ -231,6 +236,7 @@ EXTERNAL SOURCES:
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
|
||||
BVLinearGradient: b0b70acf63ee888829b7c2ebbf6b50e227396e55
|
||||
Crashlytics: 07fb167b1694128c1c9a5a5cc319b0e9c3ca0933
|
||||
DoubleConversion: bb338842f62ab1d708ceb63ec3d999f0f3d98ecd
|
||||
Fabric: f988e33c97f08930a413e08123064d2e5f68d655
|
||||
@@ -262,6 +268,6 @@ SPEC CHECKSUMS:
|
||||
SDWebImage: c5594f1a19c48d526d321e548902b56b479cd508
|
||||
yoga: b1ce48b6cf950b98deae82838f5173ea7cf89e85
|
||||
|
||||
PODFILE CHECKSUM: b5218184626a027e8b1ca12361d46100e2fa2f1f
|
||||
PODFILE CHECKSUM: 7d1909450626f31f9ea2de80122a66a50af2e1ea
|
||||
|
||||
COCOAPODS: 1.5.3
|
||||
|
||||
@@ -18,10 +18,6 @@
|
||||
|
||||
#import <JitsiMeet/JitsiMeet.h>
|
||||
|
||||
@interface ViewController
|
||||
: UIViewController<
|
||||
JitsiMeetViewDelegate,
|
||||
JMAddPeopleControllerDelegate,
|
||||
JMInviteControllerDelegate>
|
||||
@interface ViewController : UIViewController<JitsiMeetViewDelegate>
|
||||
|
||||
@end
|
||||
|
||||
@@ -24,15 +24,8 @@
|
||||
// Needed for NSUserActivity suggestedInvocationPhrase
|
||||
@import Intents;
|
||||
|
||||
/**
|
||||
* The query to perform through JMAddPeopleController when the InviteButton is
|
||||
* tapped in order to exercise the public API of the feature invite. If nil, the
|
||||
* InviteButton will not be rendered.
|
||||
*/
|
||||
static NSString * const ADD_PEOPLE_CONTROLLER_QUERY = nil;
|
||||
|
||||
@interface ViewController ()
|
||||
|
||||
@end
|
||||
|
||||
@implementation ViewController
|
||||
@@ -43,17 +36,6 @@ static NSString * const ADD_PEOPLE_CONTROLLER_QUERY = nil;
|
||||
JitsiMeetView *view = (JitsiMeetView *) self.view;
|
||||
view.delegate = self;
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
// inviteController
|
||||
JMInviteController *inviteController = view.inviteController;
|
||||
inviteController.delegate = self;
|
||||
inviteController.addPeopleEnabled
|
||||
= inviteController.dialOutEnabled
|
||||
= ADD_PEOPLE_CONTROLLER_QUERY != nil;
|
||||
|
||||
#endif // #ifdef DEBUG
|
||||
|
||||
// As this is the Jitsi Meet app (i.e. not the Jitsi Meet SDK), we do want
|
||||
// the Welcome page to be enabled. It defaults to disabled in the SDK at the
|
||||
// time of this writing but it is clearer to be explicit about what we want
|
||||
@@ -135,89 +117,4 @@ static NSString * const ADD_PEOPLE_CONTROLLER_QUERY = nil;
|
||||
[self _onJitsiMeetViewDelegateEvent:@"LOAD_CONFIG_ERROR" withData:data];
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
// JMInviteControllerDelegate
|
||||
|
||||
- (void)beginAddPeople:(JMAddPeopleController *)addPeopleController {
|
||||
NSLog(
|
||||
@"[%s:%d] JMInviteControllerDelegate %s",
|
||||
__FILE__, __LINE__, __FUNCTION__);
|
||||
|
||||
NSAssert(
|
||||
[NSThread isMainThread],
|
||||
@"JMInviteControllerDelegate beginAddPeople: invoked on a non-main thread");
|
||||
|
||||
NSString *query = ADD_PEOPLE_CONTROLLER_QUERY;
|
||||
JitsiMeetView *view = (JitsiMeetView *) self.view;
|
||||
JMInviteController *inviteController = view.inviteController;
|
||||
|
||||
if (query
|
||||
&& (inviteController.addPeopleEnabled
|
||||
|| inviteController.dialOutEnabled)) {
|
||||
addPeopleController.delegate = self;
|
||||
[addPeopleController performQuery:query];
|
||||
} else {
|
||||
// XXX Explicitly invoke endAddPeople on addPeopleController; otherwise,
|
||||
// it is going to be memory-leaked in the associated JMInviteController
|
||||
// and no subsequent InviteButton clicks/taps will be delivered.
|
||||
[addPeopleController endAddPeople];
|
||||
}
|
||||
}
|
||||
|
||||
// JMAddPeopleControllerDelegate
|
||||
|
||||
- (void)addPeopleController:(JMAddPeopleController * _Nonnull)controller
|
||||
didReceiveResults:(NSArray<NSDictionary *> * _Nonnull)results
|
||||
forQuery:(NSString * _Nonnull)query {
|
||||
NSAssert(
|
||||
[NSThread isMainThread],
|
||||
@"JMAddPeopleControllerDelegate addPeopleController:didReceiveResults:forQuery: invoked on a non-main thread");
|
||||
|
||||
NSUInteger count = results.count;
|
||||
|
||||
if (count) {
|
||||
// Exercise JMAddPeopleController's inviteById: implementation.
|
||||
NSMutableArray *ids = [NSMutableArray arrayWithCapacity:count];
|
||||
|
||||
for (NSUInteger i = 0; i < count; ++i) {
|
||||
ids[i] = results[i][@"id"];
|
||||
}
|
||||
|
||||
[controller inviteById:ids];
|
||||
|
||||
// Exercise JMInviteController's invite:withCompletion: implementation.
|
||||
//
|
||||
// XXX Technically, only at most one of the two exercises will result in
|
||||
// an actual invitation eventually.
|
||||
JitsiMeetView *view = (JitsiMeetView *) self.view;
|
||||
JMInviteController *inviteController = view.inviteController;
|
||||
|
||||
[inviteController invite:results withCompletion:nil];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// XXX Explicitly invoke endAddPeople on addPeopleController; otherwise, it
|
||||
// is going to be memory-leaked in the associated JMInviteController and no
|
||||
// subsequent InviteButton clicks/taps will be delivered.
|
||||
[controller endAddPeople];
|
||||
}
|
||||
|
||||
- (void) inviteSettled:(NSArray<NSDictionary *> * _Nonnull)failedInvitees
|
||||
fromSearchController:(JMAddPeopleController * _Nonnull)addPeopleController {
|
||||
NSAssert(
|
||||
[NSThread isMainThread],
|
||||
@"JMAddPeopleControllerDelegate inviteSettled:fromSearchController: invoked on a non-main thread");
|
||||
|
||||
// XXX Explicitly invoke endAddPeople on addPeopleController; otherwise, it
|
||||
// is going to be memory-leaked in the associated JMInviteController and no
|
||||
// subsequent InviteButton clicks/taps will be delivered. Technically,
|
||||
// endAddPeople will automatically be invoked if there are no
|
||||
// failedInviteees i.e. the invite succeeeded for all specified invitees.
|
||||
[addPeopleController endAddPeople];
|
||||
}
|
||||
|
||||
#endif // #ifdef DEBUG
|
||||
|
||||
@end
|
||||
|
||||
@@ -35,13 +35,6 @@
|
||||
87FE6F3321E52437004A5DC7 /* incomingMessage.wav in Resources */ = {isa = PBXBuildFile; fileRef = 87FE6F3221E52437004A5DC7 /* incomingMessage.wav */; };
|
||||
A4414AE020B37F1A003546E6 /* rejected.wav in Resources */ = {isa = PBXBuildFile; fileRef = A4414ADF20B37F1A003546E6 /* rejected.wav */; };
|
||||
A4A934E9212F3ADB001E9388 /* Dropbox.m in Sources */ = {isa = PBXBuildFile; fileRef = A4A934E8212F3ADB001E9388 /* Dropbox.m */; };
|
||||
B386B85720981A75000DEF7A /* InviteController.m in Sources */ = {isa = PBXBuildFile; fileRef = B386B85020981A74000DEF7A /* InviteController.m */; };
|
||||
B386B85820981A75000DEF7A /* AddPeopleController.m in Sources */ = {isa = PBXBuildFile; fileRef = B386B85120981A74000DEF7A /* AddPeopleController.m */; };
|
||||
B386B85920981A75000DEF7A /* AddPeopleControllerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = B386B85220981A74000DEF7A /* AddPeopleControllerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B386B85A20981A75000DEF7A /* AddPeopleController.h in Headers */ = {isa = PBXBuildFile; fileRef = B386B85320981A74000DEF7A /* AddPeopleController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B386B85B20981A75000DEF7A /* InviteControllerDelegate.h in Headers */ = {isa = PBXBuildFile; fileRef = B386B85420981A74000DEF7A /* InviteControllerDelegate.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B386B85C20981A75000DEF7A /* InviteController.h in Headers */ = {isa = PBXBuildFile; fileRef = B386B85520981A75000DEF7A /* InviteController.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
B386B85D20981A75000DEF7A /* Invite.m in Sources */ = {isa = PBXBuildFile; fileRef = B386B85620981A75000DEF7A /* Invite.m */; };
|
||||
C6245F5D2053091D0040BE68 /* image-resize@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = C6245F5B2053091D0040BE68 /* image-resize@2x.png */; };
|
||||
C6245F5E2053091D0040BE68 /* image-resize@3x.png in Resources */ = {isa = PBXBuildFile; fileRef = C6245F5C2053091D0040BE68 /* image-resize@3x.png */; };
|
||||
C69EFA0C209A0F660027712B /* JMCallKitEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69EFA09209A0F650027712B /* JMCallKitEmitter.swift */; };
|
||||
@@ -61,9 +54,6 @@
|
||||
0B44A0181F902126009D1D64 /* MPVolumeViewManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = MPVolumeViewManager.m; sourceTree = "<group>"; };
|
||||
0B49424320AD8DBD00BD2DE0 /* outgoingStart.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = outgoingStart.wav; path = ../../sounds/outgoingStart.wav; sourceTree = "<group>"; };
|
||||
0B49424420AD8DBD00BD2DE0 /* outgoingRinging.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = outgoingRinging.wav; path = ../../sounds/outgoingRinging.wav; sourceTree = "<group>"; };
|
||||
0B6F414F20987DE600FF6789 /* Invite+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Invite+Private.h"; sourceTree = "<group>"; };
|
||||
0B6F41502098840600FF6789 /* InviteController+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "InviteController+Private.h"; sourceTree = "<group>"; };
|
||||
0B6F4151209884E500FF6789 /* AddPeopleController+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "AddPeopleController+Private.h"; sourceTree = "<group>"; };
|
||||
0B7C2CFC200F51D60060D076 /* LaunchOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LaunchOptions.m; sourceTree = "<group>"; };
|
||||
0B93EF7A1EC608550030D24D /* CoreText.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreText.framework; path = System/Library/Frameworks/CoreText.framework; sourceTree = SDKROOT; };
|
||||
0B93EF7C1EC9DDCD0030D24D /* RCTBridgeWrapper.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTBridgeWrapper.h; sourceTree = "<group>"; };
|
||||
@@ -92,13 +82,6 @@
|
||||
A4414ADF20B37F1A003546E6 /* rejected.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; name = rejected.wav; path = ../../sounds/rejected.wav; sourceTree = "<group>"; };
|
||||
A4A934E8212F3ADB001E9388 /* Dropbox.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Dropbox.m; sourceTree = "<group>"; };
|
||||
A4A934EB21349A06001E9388 /* Dropbox.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Dropbox.h; sourceTree = "<group>"; };
|
||||
B386B85020981A74000DEF7A /* InviteController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = InviteController.m; sourceTree = "<group>"; };
|
||||
B386B85120981A74000DEF7A /* AddPeopleController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AddPeopleController.m; sourceTree = "<group>"; };
|
||||
B386B85220981A74000DEF7A /* AddPeopleControllerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddPeopleControllerDelegate.h; sourceTree = "<group>"; };
|
||||
B386B85320981A74000DEF7A /* AddPeopleController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AddPeopleController.h; sourceTree = "<group>"; };
|
||||
B386B85420981A74000DEF7A /* InviteControllerDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InviteControllerDelegate.h; sourceTree = "<group>"; };
|
||||
B386B85520981A75000DEF7A /* InviteController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InviteController.h; sourceTree = "<group>"; };
|
||||
B386B85620981A75000DEF7A /* Invite.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Invite.m; sourceTree = "<group>"; };
|
||||
C6245F5B2053091D0040BE68 /* image-resize@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "image-resize@2x.png"; path = "src/picture-in-picture/image-resize@2x.png"; sourceTree = "<group>"; };
|
||||
C6245F5C2053091D0040BE68 /* image-resize@3x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = "image-resize@3x.png"; path = "src/picture-in-picture/image-resize@3x.png"; sourceTree = "<group>"; };
|
||||
C69EFA09209A0F650027712B /* JMCallKitEmitter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JMCallKitEmitter.swift; sourceTree = "<group>"; };
|
||||
@@ -171,7 +154,6 @@
|
||||
A4A934E7212F3AB8001E9388 /* dropbox */,
|
||||
0BA13D301EE83FF8007BEF7F /* ExternalAPI.m */,
|
||||
0BD906E91EC0C00300C8C18E /* Info.plist */,
|
||||
B386B84F20981A11000DEF7A /* invite */,
|
||||
0BD906E81EC0C00300C8C18E /* JitsiMeet.h */,
|
||||
0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
|
||||
0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
|
||||
@@ -210,23 +192,6 @@
|
||||
path = dropbox;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B386B84F20981A11000DEF7A /* invite */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B386B85320981A74000DEF7A /* AddPeopleController.h */,
|
||||
B386B85120981A74000DEF7A /* AddPeopleController.m */,
|
||||
0B6F4151209884E500FF6789 /* AddPeopleController+Private.h */,
|
||||
B386B85220981A74000DEF7A /* AddPeopleControllerDelegate.h */,
|
||||
B386B85620981A75000DEF7A /* Invite.m */,
|
||||
0B6F414F20987DE600FF6789 /* Invite+Private.h */,
|
||||
B386B85520981A75000DEF7A /* InviteController.h */,
|
||||
B386B85020981A74000DEF7A /* InviteController.m */,
|
||||
0B6F41502098840600FF6789 /* InviteController+Private.h */,
|
||||
B386B85420981A74000DEF7A /* InviteControllerDelegate.h */,
|
||||
);
|
||||
path = invite;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C5E72ADFC30ED96F9B35F076 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -263,14 +228,10 @@
|
||||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
B386B85C20981A75000DEF7A /* InviteController.h in Headers */,
|
||||
B386B85B20981A75000DEF7A /* InviteControllerDelegate.h in Headers */,
|
||||
C6F99C15204DB63E0001F710 /* JitsiMeetView+Private.h in Headers */,
|
||||
0B412F181EDEC65D00B1A0A6 /* JitsiMeetView.h in Headers */,
|
||||
B386B85920981A75000DEF7A /* AddPeopleControllerDelegate.h in Headers */,
|
||||
0B93EF7E1EC9DDCD0030D24D /* RCTBridgeWrapper.h in Headers */,
|
||||
0B412F221EDEF6EA00B1A0A6 /* JitsiMeetViewDelegate.h in Headers */,
|
||||
B386B85A20981A75000DEF7A /* AddPeopleController.h in Headers */,
|
||||
0BD906EA1EC0C00300C8C18E /* JitsiMeet.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -466,12 +427,9 @@
|
||||
0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
|
||||
0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
|
||||
0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */,
|
||||
B386B85D20981A75000DEF7A /* Invite.m in Sources */,
|
||||
0B7C2CFD200F51D60060D076 /* LaunchOptions.m in Sources */,
|
||||
C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */,
|
||||
B386B85720981A75000DEF7A /* InviteController.m in Sources */,
|
||||
DEFC743F21B178FA00E4DD96 /* LocaleDetector.m in Sources */,
|
||||
B386B85820981A75000DEF7A /* AddPeopleController.m in Sources */,
|
||||
0BCA495F1EC4B6C600B793EE /* AudioMode.m in Sources */,
|
||||
0B44A0191F902126009D1D64 /* MPVolumeViewManager.m in Sources */,
|
||||
0BCA49611EC4B6C600B793EE /* Proximity.m in Sources */,
|
||||
|
||||
@@ -20,6 +20,12 @@
|
||||
#import <React/RCTBridgeModule.h>
|
||||
#import <React/RCTLog.h>
|
||||
|
||||
typedef enum {
|
||||
kAudioModeDefault,
|
||||
kAudioModeAudioCall,
|
||||
kAudioModeVideoCall
|
||||
} JitsiMeetAudioMode;
|
||||
|
||||
@interface AudioMode : NSObject<RCTBridgeModule>
|
||||
|
||||
@property(nonatomic, strong) dispatch_queue_t workerQueue;
|
||||
@@ -27,18 +33,13 @@
|
||||
@end
|
||||
|
||||
@implementation AudioMode {
|
||||
NSString *_category;
|
||||
NSString *_mode;
|
||||
NSString *_avCategory;
|
||||
NSString *_avMode;
|
||||
JitsiMeetAudioMode _mode;
|
||||
}
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
typedef enum {
|
||||
kAudioModeDefault,
|
||||
kAudioModeAudioCall,
|
||||
kAudioModeVideoCall
|
||||
} JitsiMeetAudioMode;
|
||||
|
||||
+ (BOOL)requiresMainQueueSetup {
|
||||
return NO;
|
||||
}
|
||||
@@ -54,13 +55,23 @@ typedef enum {
|
||||
- (instancetype)init {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_category = nil;
|
||||
_mode = nil;
|
||||
_avCategory = nil;
|
||||
_avMode = nil;
|
||||
_mode = kAudioModeDefault;
|
||||
|
||||
dispatch_queue_attr_t attributes =
|
||||
dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL,
|
||||
QOS_CLASS_USER_INITIATED, -1);
|
||||
_workerQueue = dispatch_queue_create("AudioMode.queue", attributes);
|
||||
|
||||
// AVAudioSession is a singleton and other parts of the application such as
|
||||
// WebRTC may undo the settings. Make sure that the settings are reapplied
|
||||
// upon undoes.
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver:self
|
||||
selector:@selector(routeChanged:)
|
||||
name:AVAudioSessionRouteChangeNotification
|
||||
object:nil];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
@@ -82,7 +93,7 @@ typedef enum {
|
||||
// needed. This notification is posted on a secondary thread, so make
|
||||
// sure we switch to our worker thread.
|
||||
dispatch_async(_workerQueue, ^{
|
||||
[self setCategory:self->_category mode:self->_mode error:nil];
|
||||
[self setCategory:self->_avCategory mode:self->_avMode error:nil];
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -97,6 +108,18 @@ typedef enum {
|
||||
error:(NSError * _Nullable *)outError {
|
||||
AVAudioSession *session = [AVAudioSession sharedInstance];
|
||||
|
||||
// We don't want to touch the category when setting the default mode.
|
||||
// This is to play well with other components which could be integrated
|
||||
// into the final application.
|
||||
if (_mode == kAudioModeDefault) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
// Nothing to do.
|
||||
if (category == nil && mode == nil) {
|
||||
return YES;
|
||||
}
|
||||
|
||||
if (session.category != category
|
||||
&& ![session setCategory:category error:outError]) {
|
||||
RCTLogError(@"Failed to (re)apply specified AVAudioSession category!");
|
||||
@@ -114,8 +137,8 @@ typedef enum {
|
||||
RCT_EXPORT_METHOD(setMode:(int)mode
|
||||
resolve:(RCTPromiseResolveBlock)resolve
|
||||
reject:(RCTPromiseRejectBlock)reject) {
|
||||
NSString *avCategory;
|
||||
NSString *avMode;
|
||||
NSString *avCategory = nil;
|
||||
NSString *avMode = nil;
|
||||
NSError *error;
|
||||
|
||||
switch (mode) {
|
||||
@@ -124,8 +147,6 @@ RCT_EXPORT_METHOD(setMode:(int)mode
|
||||
avMode = AVAudioSessionModeVoiceChat;
|
||||
break;
|
||||
case kAudioModeDefault:
|
||||
avCategory = AVAudioSessionCategorySoloAmbient;
|
||||
avMode = AVAudioSessionModeDefault;
|
||||
break;
|
||||
case kAudioModeVideoCall:
|
||||
avCategory = AVAudioSessionCategoryPlayAndRecord;
|
||||
@@ -136,29 +157,17 @@ RCT_EXPORT_METHOD(setMode:(int)mode
|
||||
return;
|
||||
}
|
||||
|
||||
// Save the desired/specified category and mode so that they may be
|
||||
// reapplied.
|
||||
_avCategory = avCategory;
|
||||
_avMode = avMode;
|
||||
_mode = mode;
|
||||
|
||||
if (![self setCategory:avCategory mode:avMode error:&error] || error) {
|
||||
reject(@"setMode", error.localizedDescription, error);
|
||||
return;
|
||||
} else {
|
||||
resolve(nil);
|
||||
}
|
||||
|
||||
// Even though the specified category and mode were successfully set, the
|
||||
// AVAudioSession is a singleton and other parts of the application such as
|
||||
// WebRTC may undo the settings. Make sure that the settings are reapplied
|
||||
// upon undoes.
|
||||
if (!_category || !_mode) {
|
||||
[[NSNotificationCenter defaultCenter]
|
||||
addObserver:self
|
||||
selector:@selector(routeChanged:)
|
||||
name:AVAudioSessionRouteChangeNotification
|
||||
object:nil];
|
||||
}
|
||||
|
||||
// Save the desired/specified category and mode so that they may be
|
||||
// reapplied (upon undoes as described above).
|
||||
_category = avCategory;
|
||||
_mode = avMode;
|
||||
|
||||
resolve(nil);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -17,9 +17,3 @@
|
||||
// JitsiMeetView
|
||||
#import <JitsiMeet/JitsiMeetView.h>
|
||||
#import <JitsiMeet/JitsiMeetViewDelegate.h>
|
||||
|
||||
// invite/
|
||||
#import <JitsiMeet/AddPeopleController.h>
|
||||
#import <JitsiMeet/AddPeopleControllerDelegate.h>
|
||||
#import <JitsiMeet/InviteController.h>
|
||||
#import <JitsiMeet/InviteControllerDelegate.h>
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "InviteController.h"
|
||||
#import "JitsiMeetViewDelegate.h"
|
||||
|
||||
@interface JitsiMeetView : UIView
|
||||
@@ -28,8 +27,6 @@
|
||||
|
||||
@property (nonatomic, nullable, weak) id<JitsiMeetViewDelegate> delegate;
|
||||
|
||||
@property (nonatomic, readonly, nonnull) JMInviteController *inviteController;
|
||||
|
||||
@property (nonatomic) BOOL pictureInPictureEnabled;
|
||||
|
||||
@property (nonatomic) BOOL welcomePageEnabled;
|
||||
|
||||
@@ -25,8 +25,6 @@
|
||||
#import <RNGoogleSignin/RNGoogleSignin.h>
|
||||
|
||||
#import "Dropbox.h"
|
||||
#import "Invite+Private.h"
|
||||
#import "InviteController+Private.h"
|
||||
#import "JitsiMeetView+Private.h"
|
||||
#import "RCTBridgeWrapper.h"
|
||||
|
||||
@@ -237,9 +235,6 @@ static NSMapTable<NSString *, JitsiMeetView *> *views;
|
||||
props[@"pictureInPictureEnabled"] = @(self.pictureInPictureEnabled);
|
||||
props[@"welcomePageEnabled"] = @(self.welcomePageEnabled);
|
||||
|
||||
props[@"addPeopleEnabled"] = @(_inviteController.addPeopleEnabled);
|
||||
props[@"dialOutEnabled"] = @(_inviteController.dialOutEnabled);
|
||||
|
||||
// XXX If urlObject is nil, then it must appear as undefined in the
|
||||
// JavaScript source code so that we check the launchOptions there.
|
||||
if (urlObject) {
|
||||
@@ -420,10 +415,6 @@ static NSMapTable<NSString *, JitsiMeetView *> *views;
|
||||
externalAPIScope = [NSUUID UUID].UUIDString;
|
||||
[views setObject:self forKey:externalAPIScope];
|
||||
|
||||
_inviteController
|
||||
= [[JMInviteController alloc] initWithExternalAPIScope:externalAPIScope
|
||||
bridgeWrapper:bridgeWrapper];
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -1,33 +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.
|
||||
*/
|
||||
|
||||
#import "AddPeopleController.h"
|
||||
#import "InviteController.h"
|
||||
|
||||
@interface JMAddPeopleController ()
|
||||
|
||||
@property (nonatomic, strong) NSMutableDictionary* _Nonnull items;
|
||||
@property (nonatomic, weak, nullable) JMInviteController *owner;
|
||||
@property (nonatomic, readonly) NSString* _Nonnull uuid;
|
||||
|
||||
- (instancetype _Nonnull)initWithOwner:(JMInviteController * _Nonnull)owner;
|
||||
|
||||
- (void)inviteSettled:(NSArray<NSDictionary *> * _Nonnull)failedInvitees;
|
||||
|
||||
- (void)receivedResults:(NSArray<NSDictionary*> * _Nonnull)results
|
||||
forQuery:(NSString * _Nonnull)query;
|
||||
|
||||
@end
|
||||
@@ -1,31 +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.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "AddPeopleControllerDelegate.h"
|
||||
|
||||
@interface JMAddPeopleController: NSObject
|
||||
|
||||
@property (nonatomic, nullable, weak) id<JMAddPeopleControllerDelegate> delegate;
|
||||
|
||||
- (void)endAddPeople;
|
||||
|
||||
- (void)inviteById:(NSArray<NSString *> * _Nonnull)ids;
|
||||
|
||||
- (void)performQuery:(NSString * _Nonnull)query;
|
||||
|
||||
@end
|
||||
@@ -1,82 +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.
|
||||
*/
|
||||
|
||||
#import "AddPeopleController+Private.h"
|
||||
#import "InviteController+Private.h"
|
||||
|
||||
@implementation JMAddPeopleController
|
||||
|
||||
- (instancetype)initWithOwner:(JMInviteController *)owner {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_uuid = [[NSUUID UUID] UUIDString];
|
||||
_items = [[NSMutableDictionary alloc] init];
|
||||
_owner = owner;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark API
|
||||
|
||||
- (void)endAddPeople {
|
||||
[self.owner endAddPeopleForController:self];
|
||||
}
|
||||
|
||||
- (void)inviteById:(NSArray<NSString *> * _Nonnull)ids {
|
||||
NSMutableArray* invitees = [[NSMutableArray alloc] init];
|
||||
|
||||
for (NSString* itemId in ids) {
|
||||
id invitee = [self.items objectForKey:itemId];
|
||||
|
||||
if (invitee) {
|
||||
[invitees addObject:invitee];
|
||||
}
|
||||
}
|
||||
|
||||
[self.owner invite:invitees forController:self];
|
||||
}
|
||||
|
||||
- (void)performQuery:(NSString *)query {
|
||||
[self.owner performQuery:query forController:self];
|
||||
}
|
||||
|
||||
#pragma mark Internal API, used to call the delegate and report to the user
|
||||
|
||||
- (void)receivedResults:(NSArray<NSDictionary *> *)results
|
||||
forQuery:(NSString *)query {
|
||||
for (NSDictionary* item in results) {
|
||||
NSString* itemId = item[@"id"];
|
||||
NSString* itemType = item[@"type"];
|
||||
if (itemId) {
|
||||
[self.items setObject:item forKey:itemId];
|
||||
} else if (itemType != nil && [itemType isEqualToString: @"phone"]) {
|
||||
NSString* number = item[@"number"];
|
||||
if (number) {
|
||||
[self.items setObject:item forKey:number];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[self.delegate addPeopleController:self
|
||||
didReceiveResults:results
|
||||
forQuery:query];
|
||||
}
|
||||
|
||||
- (void)inviteSettled:(NSArray<NSDictionary *> *)failedInvitees {
|
||||
[self.delegate inviteSettled:failedInvitees fromSearchController:self];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,41 +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.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "AddPeopleController.h"
|
||||
|
||||
@class JMAddPeopleController;
|
||||
|
||||
@protocol JMAddPeopleControllerDelegate
|
||||
|
||||
/**
|
||||
* Called when a JMAddPeopleController has results for a query that was
|
||||
* previously provided.
|
||||
*/
|
||||
- (void)addPeopleController:(JMAddPeopleController * _Nonnull)controller
|
||||
didReceiveResults:(NSArray<NSDictionary *> * _Nonnull)results
|
||||
forQuery:(NSString * _Nonnull)query;
|
||||
|
||||
/**
|
||||
* Called when a JMAddPeopleController has finished the inviting process, either
|
||||
* succesfully or not. In case of failure the failedInvitees array will contain
|
||||
* the items for which invitations failed.
|
||||
*/
|
||||
- (void) inviteSettled:(NSArray<NSDictionary *> * _Nonnull)failedInvitees
|
||||
fromSearchController:(JMAddPeopleController * _Nonnull)addPeopleController;
|
||||
|
||||
@end
|
||||
@@ -1,30 +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.
|
||||
*/
|
||||
|
||||
#import <React/RCTBridge.h>
|
||||
#import <React/RCTEventEmitter.h>
|
||||
|
||||
@interface Invite : RCTEventEmitter <RCTBridgeModule>
|
||||
|
||||
- (void) invite:(NSArray<NSDictionary *> * _Nonnull)invitees
|
||||
externalAPIScope:(NSString * _Nonnull)externalAPIScope
|
||||
addPeopleControllerScope:(NSString * _Nonnull)addPeopleControllerScope;
|
||||
|
||||
- (void) performQuery:(NSString * _Nonnull)query
|
||||
externalAPIScope:(NSString * _Nonnull)externalAPIScope
|
||||
addPeopleControllerScope:(NSString * _Nonnull)addPeopleControllerScope;
|
||||
|
||||
@end
|
||||
@@ -1,127 +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.
|
||||
*/
|
||||
|
||||
#import "Invite+Private.h"
|
||||
#import "InviteController+Private.h"
|
||||
#import "JitsiMeetView+Private.h"
|
||||
|
||||
// The events emitted/supported by the Invite react-native module:
|
||||
//
|
||||
// XXX The event names are ridiculous on purpose. Even though iOS makes it look
|
||||
// like it emits within the bounderies of a react-native module ony, it actually
|
||||
// also emits through DeviceEventEmitter. (Of course, Android emits only through
|
||||
// DeviceEventEmitter.)
|
||||
static NSString * const InviteEmitterEvent
|
||||
= @"org.jitsi.meet:features/invite#invite";
|
||||
static NSString * const PerformQueryEmitterEvent
|
||||
= @"org.jitsi.meet:features/invite#performQuery";
|
||||
|
||||
@implementation Invite
|
||||
|
||||
RCT_EXPORT_MODULE();
|
||||
|
||||
/**
|
||||
* Make sure all methods in this module are invoked on the main/UI thread.
|
||||
*/
|
||||
- (dispatch_queue_t)methodQueue {
|
||||
return dispatch_get_main_queue();
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)supportedEvents {
|
||||
return @[
|
||||
InviteEmitterEvent,
|
||||
PerformQueryEmitterEvent
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiates the process to add people. This involves calling a delegate method
|
||||
* in the JMInviteControllerDelegate so the native host application can start
|
||||
* the query process.
|
||||
*
|
||||
* @param externalAPIScope - Scope identifying the JitsiMeetView where the
|
||||
* calling JS code is being executed.
|
||||
*/
|
||||
RCT_EXPORT_METHOD(beginAddPeople:(NSString *)externalAPIScope) {
|
||||
JitsiMeetView *view
|
||||
= [JitsiMeetView viewForExternalAPIScope:externalAPIScope];
|
||||
JMInviteController *inviteController = view.inviteController;
|
||||
[inviteController beginAddPeople];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates the the invite process has settled / finished.
|
||||
*
|
||||
* @param externalAPIScope - Scope identifying the JitsiMeetView where the
|
||||
* calling JS code is being executed.
|
||||
* @param addPeopleControllerScope - Scope identifying the JMAddPeopleController
|
||||
* wich was settled.
|
||||
* @param failedInvitees - Array with the invitees which were not invited due
|
||||
* to a failure.
|
||||
*/
|
||||
RCT_EXPORT_METHOD(inviteSettled:(NSString *)externalAPIScope
|
||||
addPeopleControllerScope:(NSString *)addPeopleControllerScope
|
||||
failedInvitees:(NSArray *)failedInvitees) {
|
||||
JitsiMeetView *view
|
||||
= [JitsiMeetView viewForExternalAPIScope:externalAPIScope];
|
||||
JMInviteController *inviteController = view.inviteController;
|
||||
[inviteController inviteSettled:addPeopleControllerScope
|
||||
failedInvitees:failedInvitees];
|
||||
}
|
||||
|
||||
/**
|
||||
* Process results received for the given query. This involves calling a
|
||||
* delegate method in JMAddPeopleControllerDelegate so the native host
|
||||
* application is made aware of the query results.
|
||||
*
|
||||
* @param externalAPIScope - Scope identifying the JitsiMeetView where the
|
||||
* calling JS code is being executed.
|
||||
* @param addPeopleControllerScope - Scope identifying the JMAddPeopleController
|
||||
* for which the results were received.
|
||||
* @param query - The actual query for which the results were received.
|
||||
* @param results - The query results.
|
||||
*/
|
||||
RCT_EXPORT_METHOD(receivedResults:(NSString *)externalAPIScope
|
||||
addPeopleControllerScope:(NSString *)addPeopleControllerScope
|
||||
query:(NSString *)query
|
||||
results:(NSArray *)results) {
|
||||
JitsiMeetView *view
|
||||
= [JitsiMeetView viewForExternalAPIScope:externalAPIScope];
|
||||
JMInviteController *inviteController = view.inviteController;
|
||||
[inviteController receivedResults:addPeopleControllerScope
|
||||
query:query
|
||||
results:results];
|
||||
}
|
||||
|
||||
- (void) invite:(NSArray<NSDictionary *> * _Nonnull)invitees
|
||||
externalAPIScope:(NSString * _Nonnull)externalAPIScope
|
||||
addPeopleControllerScope:(NSString * _Nonnull) addPeopleControllerScope {
|
||||
[self sendEventWithName:InviteEmitterEvent
|
||||
body:@{ @"addPeopleControllerScope": addPeopleControllerScope,
|
||||
@"externalAPIScope": externalAPIScope,
|
||||
@"invitees": invitees }];
|
||||
}
|
||||
|
||||
- (void) performQuery:(NSString * _Nonnull)query
|
||||
externalAPIScope:(NSString * _Nonnull)externalAPIScope
|
||||
addPeopleControllerScope:(NSString * _Nonnull) addPeopleControllerScope {
|
||||
[self sendEventWithName:PerformQueryEmitterEvent
|
||||
body:@{ @"addPeopleControllerScope": addPeopleControllerScope,
|
||||
@"externalAPIScope": externalAPIScope,
|
||||
@"query": query }];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,53 +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.
|
||||
*/
|
||||
|
||||
#import "InviteController.h"
|
||||
|
||||
#import "AddPeopleController.h"
|
||||
#import "Invite+Private.h"
|
||||
#import "RCTBridgeWrapper.h"
|
||||
|
||||
@interface JMInviteController ()
|
||||
|
||||
@property (nonatomic, nullable) JMAddPeopleController *addPeopleController;
|
||||
|
||||
@property (nonatomic) NSString * _Nonnull externalAPIScope;
|
||||
|
||||
@property (nonatomic, nullable, weak) RCTBridgeWrapper *bridgeWrapper;
|
||||
|
||||
@property (nonatomic, readonly) Invite * _Nullable inviteModule;
|
||||
|
||||
- (instancetype _Nonnull)initWithExternalAPIScope:(NSString * _Nonnull)externalAPIScope
|
||||
bridgeWrapper:(RCTBridgeWrapper * _Nullable)bridgeWrapper;
|
||||
|
||||
- (void)beginAddPeople;
|
||||
|
||||
- (void)endAddPeopleForController:(JMAddPeopleController * _Nonnull)controller;
|
||||
|
||||
- (void) invite:(NSArray * _Nonnull)invitees
|
||||
forController:(JMAddPeopleController * _Nonnull)controller;
|
||||
|
||||
- (void)inviteSettled:(NSString * _Nonnull)addPeopleControllerScope
|
||||
failedInvitees:(NSArray * _Nonnull)failedInvitees;
|
||||
|
||||
- (void)performQuery:(NSString * _Nonnull)query
|
||||
forController:(JMAddPeopleController * _Nonnull)controller;
|
||||
|
||||
- (void)receivedResults:(NSString * _Nonnull)addPeopleControllerScope
|
||||
query:(NSString * _Nonnull)query
|
||||
results:(NSArray * _Nonnull)results;
|
||||
|
||||
@end
|
||||
@@ -1,32 +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.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "InviteControllerDelegate.h"
|
||||
|
||||
@interface JMInviteController : NSObject
|
||||
|
||||
@property (nonatomic) BOOL addPeopleEnabled;
|
||||
|
||||
@property (nonatomic) BOOL dialOutEnabled;
|
||||
|
||||
@property (nonatomic, nullable, weak) id<JMInviteControllerDelegate> delegate;
|
||||
|
||||
- (void) invite:(NSArray * _Nonnull)invitees
|
||||
withCompletion:(void (^ _Nullable)(NSArray<NSDictionary *> * _Nonnull failedInvitees))completion;
|
||||
|
||||
@end
|
||||
@@ -1,160 +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.
|
||||
*/
|
||||
|
||||
#import "InviteController+Private.h"
|
||||
#import "AddPeopleController+Private.h"
|
||||
|
||||
@implementation JMInviteController {
|
||||
NSNumber *_addPeopleEnabled;
|
||||
NSNumber *_dialOutEnabled;
|
||||
}
|
||||
|
||||
@dynamic addPeopleEnabled;
|
||||
@dynamic dialOutEnabled;
|
||||
|
||||
#pragma mark Constructor
|
||||
|
||||
-(instancetype)initWithExternalAPIScope:(NSString * _Nonnull)externalAPIScope
|
||||
bridgeWrapper:(RCTBridgeWrapper * _Nullable)bridgeWrapper {
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.externalAPIScope = externalAPIScope;
|
||||
self.bridgeWrapper = bridgeWrapper;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark Public API
|
||||
|
||||
-(Invite * _Nullable)inviteModule {
|
||||
return [self.bridgeWrapper.bridge moduleForName:@"Invite"];
|
||||
}
|
||||
|
||||
-(void)beginAddPeople {
|
||||
if (_delegate == nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_addPeopleController != nil) {
|
||||
return;
|
||||
}
|
||||
|
||||
_addPeopleController = [[JMAddPeopleController alloc] initWithOwner:self];
|
||||
|
||||
@try {
|
||||
if (self.delegate
|
||||
&& [self.delegate respondsToSelector:@selector(beginAddPeople:)]) {
|
||||
[self.delegate beginAddPeople:_addPeopleController];
|
||||
}
|
||||
} @catch (NSException *e) {
|
||||
[self endAddPeopleForController:_addPeopleController];
|
||||
}
|
||||
}
|
||||
|
||||
-(void)endAddPeopleForController:(JMAddPeopleController *)controller {
|
||||
if (self.addPeopleController == controller) {
|
||||
self.addPeopleController = nil;
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Property getters / setters
|
||||
|
||||
- (void) setAddPeopleEnabled:(BOOL)addPeopleEnabled {
|
||||
_addPeopleEnabled = [NSNumber numberWithBool:addPeopleEnabled];
|
||||
}
|
||||
|
||||
- (BOOL) addPeopleEnabled {
|
||||
if (_addPeopleEnabled == nil || [_addPeopleEnabled boolValue]) {
|
||||
return self.delegate
|
||||
&& [self.delegate respondsToSelector:@selector(beginAddPeople:)];
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void) setDialOutEnabled:(BOOL)dialOutEnabled {
|
||||
_dialOutEnabled = [NSNumber numberWithBool:dialOutEnabled];
|
||||
}
|
||||
|
||||
- (BOOL) dialOutEnabled {
|
||||
if (_dialOutEnabled == nil || [_dialOutEnabled boolValue]) {
|
||||
return self.delegate
|
||||
&& [self.delegate respondsToSelector:@selector(beginAddPeople:)];
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
#pragma mark Result handling
|
||||
|
||||
- (void)inviteSettled:(NSString *)addPeopleControllerScope
|
||||
failedInvitees:(NSArray *)failedInvitees {
|
||||
JMAddPeopleController *controller = self.addPeopleController;
|
||||
|
||||
if (controller != nil
|
||||
&& [controller.uuid isEqualToString:addPeopleControllerScope]) {
|
||||
@try {
|
||||
[controller inviteSettled:failedInvitees];
|
||||
} @finally {
|
||||
if ([failedInvitees count] == 0) {
|
||||
[self endAddPeopleForController:controller];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)receivedResults:(NSString *)addPeopleControllerScope
|
||||
query:(NSString *)query
|
||||
results:(NSArray *)results {
|
||||
JMAddPeopleController *controller = self.addPeopleController;
|
||||
|
||||
if (controller != nil
|
||||
&& [controller.uuid isEqualToString:addPeopleControllerScope]) {
|
||||
[controller receivedResults:results forQuery:query];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark Use the Invite react-native module to emit the search / submission events
|
||||
|
||||
- (void) invite:(NSArray *)invitees
|
||||
forController:(JMAddPeopleController * _Nonnull)controller {
|
||||
[self invite:invitees
|
||||
forControllerScope:controller.uuid];
|
||||
}
|
||||
|
||||
- (void) invite:(NSArray *)invitees
|
||||
forControllerScope:(NSString * _Nonnull)controllerScope {
|
||||
[self.inviteModule invite:invitees
|
||||
externalAPIScope:self.externalAPIScope
|
||||
addPeopleControllerScope:controllerScope];
|
||||
}
|
||||
|
||||
- (void) invite:(NSArray *)invitees
|
||||
withCompletion:(void (^)(NSArray<NSDictionary *> *failedInvitees))completion {
|
||||
// TODO Execute the specified completion block when the invite settles.
|
||||
[self invite:invitees
|
||||
forControllerScope:[[NSUUID UUID] UUIDString]];
|
||||
}
|
||||
|
||||
- (void)performQuery:(NSString * _Nonnull)query
|
||||
forController:(JMAddPeopleController * _Nonnull)controller {
|
||||
[self.inviteModule performQuery:query
|
||||
externalAPIScope:self.externalAPIScope
|
||||
addPeopleControllerScope:controller.uuid];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -1,29 +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.
|
||||
*/
|
||||
|
||||
#import "AddPeopleController.h"
|
||||
|
||||
@protocol JMInviteControllerDelegate <NSObject>
|
||||
|
||||
/**
|
||||
* Called when the invite button in the conference is tapped.
|
||||
*
|
||||
* The search controller provided can be used to query user search within the
|
||||
* conference.
|
||||
*/
|
||||
- (void)beginAddPeople:(JMAddPeopleController *)addPeopleController;
|
||||
|
||||
@end
|
||||
@@ -87,7 +87,8 @@ const AudioLevels = {
|
||||
|
||||
// External circle audio level.
|
||||
const ext = {
|
||||
level: ((int.level * scale * level) + int.level).toFixed(0),
|
||||
level: parseFloat(
|
||||
((int.level * scale * level) + int.level).toFixed(0)),
|
||||
color: interfaceConfig.AUDIO_LEVEL_SECONDARY_COLOR
|
||||
};
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ import { I18nextProvider } from 'react-i18next';
|
||||
import { Provider } from 'react-redux';
|
||||
|
||||
import { i18next } from '../../../react/features/base/i18n';
|
||||
import {
|
||||
Avatar,
|
||||
getAvatarURLByParticipantId
|
||||
} from '../../../react/features/base/participants';
|
||||
import { PresenceLabel } from '../../../react/features/presence-status';
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
@@ -14,9 +18,6 @@ const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
import {
|
||||
JitsiParticipantConnectionStatus
|
||||
} from '../../../react/features/base/lib-jitsi-meet';
|
||||
import {
|
||||
getAvatarURLByParticipantId
|
||||
} from '../../../react/features/base/participants';
|
||||
import {
|
||||
updateKnownLargeVideoResolution
|
||||
} from '../../../react/features/large-video';
|
||||
@@ -95,6 +96,9 @@ export default class LargeVideoManager {
|
||||
= this._onVideoResolutionUpdate.bind(this);
|
||||
|
||||
this.videoContainer.addResizeListener(this._onVideoResolutionUpdate);
|
||||
|
||||
this._dominantSpeakerAvatarContainer
|
||||
= document.getElementById('dominantSpeakerAvatarContainer');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,6 +113,8 @@ export default class LargeVideoManager {
|
||||
|
||||
this.removePresenceLabel();
|
||||
|
||||
ReactDOM.unmountComponentAtNode(this._dominantSpeakerAvatarContainer);
|
||||
|
||||
this.$container.css({ display: 'none' });
|
||||
}
|
||||
|
||||
@@ -394,7 +400,17 @@ export default class LargeVideoManager {
|
||||
* Updates the src of the dominant speaker avatar
|
||||
*/
|
||||
updateAvatar(avatarUrl) {
|
||||
$('#dominantSpeakerAvatar').attr('src', avatarUrl);
|
||||
if (avatarUrl) {
|
||||
ReactDOM.render(
|
||||
<Avatar
|
||||
id = "dominantSpeakerAvatar"
|
||||
uri = { avatarUrl } />,
|
||||
this._dominantSpeakerAvatarContainer
|
||||
);
|
||||
} else {
|
||||
ReactDOM.unmountComponentAtNode(
|
||||
this._dominantSpeakerAvatarContainer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -265,7 +265,7 @@ export class VideoContainer extends LargeContainer {
|
||||
*/
|
||||
this.$wrapperParent = this.$wrapper.parent();
|
||||
|
||||
this.avatarHeight = $('#dominantSpeakerAvatar').height();
|
||||
this.avatarHeight = $('#dominantSpeakerAvatarContainer').height();
|
||||
|
||||
const onPlayingCallback = function(event) {
|
||||
if (typeof resizeContainer === 'function') {
|
||||
@@ -408,7 +408,7 @@ export class VideoContainer extends LargeContainer {
|
||||
*/
|
||||
_positionParticipantStatus($element) {
|
||||
if (this.avatarDisplayed) {
|
||||
const $avatarImage = $('#dominantSpeakerAvatar');
|
||||
const $avatarImage = $('#dominantSpeakerAvatarContainer');
|
||||
|
||||
$element.css(
|
||||
'top',
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
} from '../../../react/features/base/lib-jitsi-meet';
|
||||
import { VIDEO_TYPE } from '../../../react/features/base/media';
|
||||
import {
|
||||
getLocalParticipant as getLocalParticipantFromStore,
|
||||
getPinnedParticipant,
|
||||
pinParticipant
|
||||
} from '../../../react/features/base/participants';
|
||||
@@ -75,6 +76,16 @@ function getAllThumbnails() {
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Private helper to get the redux representation of the local participant.
|
||||
*
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
function getLocalParticipant() {
|
||||
return getLocalParticipantFromStore(APP.store.getState());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the user ID of the remote participant that is current the dominant
|
||||
* speaker.
|
||||
@@ -181,7 +192,7 @@ const VideoLayout = {
|
||||
},
|
||||
|
||||
changeLocalVideo(stream) {
|
||||
const localId = APP.conference.getMyUserId();
|
||||
const localId = getLocalParticipant().id;
|
||||
|
||||
this.onVideoTypeChanged(localId, stream.videoType);
|
||||
|
||||
@@ -198,7 +209,7 @@ const VideoLayout = {
|
||||
*/
|
||||
mucJoined() {
|
||||
if (largeVideo && !largeVideo.id) {
|
||||
this.updateLargeVideo(APP.conference.getMyUserId(), true);
|
||||
this.updateLargeVideo(getLocalParticipant().id, true);
|
||||
}
|
||||
|
||||
// FIXME: replace this call with a generic update call once SmallVideo
|
||||
@@ -302,7 +313,7 @@ const VideoLayout = {
|
||||
// Go with local video
|
||||
logger.info('Fallback to local video...');
|
||||
|
||||
const id = APP.conference.getMyUserId();
|
||||
const { id } = getLocalParticipant();
|
||||
|
||||
logger.info(`electLastVisibleVideo: ${id}`);
|
||||
|
||||
|
||||
4
package-lock.json
generated
4
package-lock.json
generated
@@ -8431,8 +8431,8 @@
|
||||
}
|
||||
},
|
||||
"lib-jitsi-meet": {
|
||||
"version": "github:jitsi/lib-jitsi-meet#96dfda58411d8168d5edd061374120943f4fff74",
|
||||
"from": "github:jitsi/lib-jitsi-meet#96dfda58411d8168d5edd061374120943f4fff74",
|
||||
"version": "github:jitsi/lib-jitsi-meet#2e1436e20d4d8fb6020497a87b2714dff38a6c86",
|
||||
"from": "github:jitsi/lib-jitsi-meet#2e1436e20d4d8fb6020497a87b2714dff38a6c86",
|
||||
"requires": {
|
||||
"@jitsi/sdp-interop": "0.1.13",
|
||||
"@jitsi/sdp-simulcast": "0.2.1",
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
"jsc-android": "224109.1.0",
|
||||
"jsrsasign": "8.0.12",
|
||||
"jwt-decode": "2.2.0",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#96dfda58411d8168d5edd061374120943f4fff74",
|
||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#2e1436e20d4d8fb6020497a87b2714dff38a6c86",
|
||||
"libflacjs": "github:mmig/libflac.js#93d37e7f811f01cf7d8b6a603e38bd3c3810907d",
|
||||
"lodash": "4.17.11",
|
||||
"moment": "2.19.4",
|
||||
|
||||
@@ -35,16 +35,6 @@ const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
*/
|
||||
type Props = AbstractAppProps & {
|
||||
|
||||
/**
|
||||
* Whether the add people feature is enabled.
|
||||
*/
|
||||
addPeopleEnabled: boolean,
|
||||
|
||||
/**
|
||||
* Whether the dial-out feature is enabled.
|
||||
*/
|
||||
dialOutEnabled: boolean,
|
||||
|
||||
/**
|
||||
* Whether Picture-in-Picture is enabled. If {@code true}, a toolbar button
|
||||
* is rendered in the {@link Conference} view to afford entering
|
||||
|
||||
@@ -145,6 +145,22 @@ export function forEachConference(
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the display name of the conference.
|
||||
*
|
||||
* @param {Function | Object} stateful - Reference that can be resolved to Redux
|
||||
* state with the {@code toState} function.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function getConferenceName(stateful: Function | Object): string {
|
||||
const state = toState(stateful);
|
||||
const { callee } = state['features/base/jwt'];
|
||||
|
||||
return state['features/base/config'].callDisplayName
|
||||
|| (callee && callee.name)
|
||||
|| state['features/base/conference'].room;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current {@code JitsiConference} which is joining or joined and is
|
||||
* not leaving. Please note the contrast with merely reading the
|
||||
|
||||
@@ -71,7 +71,7 @@ function _fixRoom(room: ?string) {
|
||||
* @returns {string}
|
||||
*/
|
||||
function _fixURIStringScheme(uri: string) {
|
||||
const regex = new RegExp(`${URI_PROTOCOL_PATTERN}+`, 'i');
|
||||
const regex = new RegExp(`${URI_PROTOCOL_PATTERN}+`, 'gi');
|
||||
const match: Array<string> | null = regex.exec(uri);
|
||||
|
||||
if (match) {
|
||||
|
||||
3
react/features/conference/components/index.native.js
Normal file
3
react/features/conference/components/index.native.js
Normal file
@@ -0,0 +1,3 @@
|
||||
// @flow
|
||||
|
||||
export * from './native';
|
||||
3
react/features/conference/components/index.web.js
Normal file
3
react/features/conference/components/index.web.js
Normal file
@@ -0,0 +1,3 @@
|
||||
// @flow
|
||||
|
||||
export * from './web';
|
||||
@@ -5,29 +5,31 @@ import React, { Component } from 'react';
|
||||
import { BackHandler, StatusBar, View } from 'react-native';
|
||||
import { connect as reactReduxConnect } from 'react-redux';
|
||||
|
||||
import { appNavigate } from '../../app';
|
||||
import { connect, disconnect } from '../../base/connection';
|
||||
import { getParticipantCount } from '../../base/participants';
|
||||
import { Container, LoadingIndicator, TintedView } from '../../base/react';
|
||||
import { appNavigate } from '../../../app';
|
||||
import { connect, disconnect } from '../../../base/connection';
|
||||
import { getParticipantCount } from '../../../base/participants';
|
||||
import { Container, LoadingIndicator, TintedView } from '../../../base/react';
|
||||
import {
|
||||
makeAspectRatioAware
|
||||
} from '../../base/responsive-ui';
|
||||
import { TestConnectionInfo } from '../../base/testing';
|
||||
import { createDesiredLocalTracks } from '../../base/tracks';
|
||||
import { ConferenceNotification } from '../../calendar-sync';
|
||||
import { Chat } from '../../chat';
|
||||
} from '../../../base/responsive-ui';
|
||||
import { TestConnectionInfo } from '../../../base/testing';
|
||||
import { createDesiredLocalTracks } from '../../../base/tracks';
|
||||
import { ConferenceNotification } from '../../../calendar-sync';
|
||||
import { Chat } from '../../../chat';
|
||||
import {
|
||||
Filmstrip,
|
||||
isFilmstripVisible,
|
||||
TileView
|
||||
} from '../../filmstrip';
|
||||
import { LargeVideo } from '../../large-video';
|
||||
import { CalleeInfoContainer } from '../../invite';
|
||||
import { Captions } from '../../subtitles';
|
||||
import { setToolboxVisible, Toolbox } from '../../toolbox';
|
||||
import { shouldDisplayTileView } from '../../video-layout';
|
||||
} from '../../../filmstrip';
|
||||
import { LargeVideo } from '../../../large-video';
|
||||
import { CalleeInfoContainer } from '../../../invite';
|
||||
import { Captions } from '../../../subtitles';
|
||||
import { setToolboxVisible, Toolbox } from '../../../toolbox';
|
||||
import { shouldDisplayTileView } from '../../../video-layout';
|
||||
|
||||
import DisplayNameLabel from './DisplayNameLabel';
|
||||
import Labels from './Labels';
|
||||
import NavigationBar from './NavigationBar';
|
||||
import styles from './styles';
|
||||
|
||||
/**
|
||||
@@ -282,6 +284,8 @@ class Conference extends Component<Props> {
|
||||
pointerEvents = 'box-none'
|
||||
style = { styles.toolboxAndFilmstripContainer }>
|
||||
|
||||
<Labels />
|
||||
|
||||
<Captions onPress = { this._onClick } />
|
||||
|
||||
<DisplayNameLabel />
|
||||
@@ -303,6 +307,8 @@ class Conference extends Component<Props> {
|
||||
}
|
||||
</View>
|
||||
|
||||
<NavigationBar />
|
||||
|
||||
<TestConnectionInfo />
|
||||
|
||||
{
|
||||
@@ -8,9 +8,9 @@ import {
|
||||
getLocalParticipant,
|
||||
getParticipantDisplayName,
|
||||
shouldRenderParticipantVideo
|
||||
} from '../../base/participants';
|
||||
} from '../../../base/participants';
|
||||
|
||||
import { shouldDisplayTileView } from '../../video-layout';
|
||||
import { shouldDisplayTileView } from '../../../video-layout';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
@@ -4,21 +4,23 @@ import React from 'react';
|
||||
import { TouchableOpacity, View } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { JitsiRecordingConstants } from '../../base/lib-jitsi-meet';
|
||||
import {
|
||||
RecordingExpandedLabel
|
||||
} from '../../recording';
|
||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||
import {
|
||||
isNarrowAspectRatio,
|
||||
makeAspectRatioAware
|
||||
} from '../../base/responsive-ui';
|
||||
import { TranscribingExpandedLabel } from '../../transcribing';
|
||||
import { VideoQualityExpandedLabel } from '../../video-quality';
|
||||
} from '../../../base/responsive-ui';
|
||||
import {
|
||||
RecordingExpandedLabel
|
||||
} from '../../../recording';
|
||||
import { isToolboxVisible } from '../../../toolbox';
|
||||
import { TranscribingExpandedLabel } from '../../../transcribing';
|
||||
import { shouldDisplayTileView } from '../../../video-layout';
|
||||
import { VideoQualityExpandedLabel } from '../../../video-quality';
|
||||
|
||||
import AbstractLabels, {
|
||||
_abstractMapStateToProps,
|
||||
type Props as AbstractLabelsProps
|
||||
} from './AbstractLabels';
|
||||
} from '../AbstractLabels';
|
||||
import styles from './styles';
|
||||
|
||||
/**
|
||||
@@ -37,7 +39,12 @@ type Props = AbstractLabelsProps & {
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_reducedUI: boolean
|
||||
_reducedUI: boolean,
|
||||
|
||||
/**
|
||||
* True if the labels should be visible, false otherwise.
|
||||
*/
|
||||
_visible: boolean
|
||||
};
|
||||
|
||||
type State = {
|
||||
@@ -148,6 +155,10 @@ class Labels extends AbstractLabels<Props, State> {
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
if (!this.props._visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const wide = !isNarrowAspectRatio(this);
|
||||
const { _filmstripVisible, _reducedUI } = this.props;
|
||||
|
||||
@@ -344,13 +355,15 @@ class Labels extends AbstractLabels<Props, State> {
|
||||
* @private
|
||||
* @returns {{
|
||||
* _filmstripVisible: boolean,
|
||||
* _reducedUI: boolean
|
||||
* _reducedUI: boolean,
|
||||
* _visible: boolean
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
return {
|
||||
..._abstractMapStateToProps(state),
|
||||
_reducedUI: state['features/base/responsive-ui'].reducedUI
|
||||
_reducedUI: state['features/base/responsive-ui'].reducedUI,
|
||||
_visible: !isToolboxVisible(state) && !shouldDisplayTileView(state)
|
||||
};
|
||||
}
|
||||
|
||||
96
react/features/conference/components/native/NavigationBar.js
Normal file
96
react/features/conference/components/native/NavigationBar.js
Normal file
@@ -0,0 +1,96 @@
|
||||
// @flow
|
||||
|
||||
import _ from 'lodash';
|
||||
import React, { Component } from 'react';
|
||||
import { SafeAreaView, Text, View } from 'react-native';
|
||||
import LinearGradient from 'react-native-linear-gradient';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { getConferenceName } from '../../../base/conference';
|
||||
import { PictureInPictureButton } from '../../../mobile/picture-in-picture';
|
||||
import { isToolboxVisible } from '../../../toolbox';
|
||||
|
||||
import styles, { NAVBAR_GRADIENT_COLORS } from './styles';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* Name of the meeting we're currently in.
|
||||
*/
|
||||
_meetingName: string,
|
||||
|
||||
/**
|
||||
* True if the navigation bar should be visible.
|
||||
*/
|
||||
_visible: boolean
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements a navigation bar component that is rendered on top of the
|
||||
* conference screen.
|
||||
*/
|
||||
class NavigationBar extends Component<Props> {
|
||||
/**
|
||||
* Implements {@Component#render}.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
if (!this.props._visible) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<View
|
||||
pointerEvents = 'box-none'
|
||||
style = { styles.navBarContainer }>
|
||||
<LinearGradient
|
||||
colors = { NAVBAR_GRADIENT_COLORS }
|
||||
pointerEvents = 'none'
|
||||
style = { styles.gradient }>
|
||||
<SafeAreaView>
|
||||
<View style = { styles.gradientStretch } />
|
||||
</SafeAreaView>
|
||||
</LinearGradient>
|
||||
<SafeAreaView
|
||||
pointerEvents = 'box-none'
|
||||
style = { styles.navBarSafeView }>
|
||||
<View
|
||||
pointerEvents = 'box-none'
|
||||
style = { styles.navBarWrapper }>
|
||||
<PictureInPictureButton
|
||||
styles = { styles.navBarButton } />
|
||||
<View
|
||||
pointerEvents = 'box-none'
|
||||
style = { styles.roomNameWrapper }>
|
||||
<Text
|
||||
numberOfLines = { 1 }
|
||||
style = { styles.roomName }>
|
||||
{ this.props._meetingName }
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps part of the Redux store to the props of this component.
|
||||
*
|
||||
* @param {Object} state - The Redux state.
|
||||
* @returns {{
|
||||
* _meetingName: string,
|
||||
* _visible: boolean
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state) {
|
||||
return {
|
||||
_meetingName: _.startCase(getConferenceName(state)),
|
||||
_visible: isToolboxVisible(state)
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(_mapStateToProps)(NavigationBar);
|
||||
137
react/features/conference/components/native/styles.js
Normal file
137
react/features/conference/components/native/styles.js
Normal file
@@ -0,0 +1,137 @@
|
||||
import {
|
||||
BoxModel,
|
||||
ColorPalette,
|
||||
createStyleSheet,
|
||||
fixAndroidViewClipping
|
||||
} from '../../../base/styles';
|
||||
|
||||
import { FILMSTRIP_SIZE } from '../../../filmstrip';
|
||||
|
||||
export const NAVBAR_GRADIENT_COLORS = [ 'black', '#00000000' ];
|
||||
|
||||
/**
|
||||
* The styles of the feature conference.
|
||||
*/
|
||||
export default createStyleSheet({
|
||||
/**
|
||||
* {@code Conference} style.
|
||||
*/
|
||||
conference: fixAndroidViewClipping({
|
||||
alignSelf: 'stretch',
|
||||
backgroundColor: ColorPalette.appBackground,
|
||||
flex: 1
|
||||
}),
|
||||
|
||||
displayNameBackdrop: {
|
||||
alignSelf: 'center',
|
||||
backgroundColor: 'rgba(28, 32, 37, 0.6)',
|
||||
borderRadius: 4,
|
||||
margin: 16,
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 4
|
||||
},
|
||||
|
||||
displayNameText: {
|
||||
color: ColorPalette.white,
|
||||
fontSize: 14
|
||||
},
|
||||
|
||||
gradient: {
|
||||
flex: 1
|
||||
},
|
||||
|
||||
gradientStretch: {
|
||||
height: 116
|
||||
},
|
||||
|
||||
/**
|
||||
* View that contains the indicators.
|
||||
*/
|
||||
indicatorContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
margin: BoxModel.margin
|
||||
},
|
||||
|
||||
/**
|
||||
* Indicator container for wide aspect ratio.
|
||||
*/
|
||||
indicatorContainerWide: {
|
||||
marginRight: FILMSTRIP_SIZE + BoxModel.margin
|
||||
},
|
||||
|
||||
labelWrapper: {
|
||||
flexDirection: 'column',
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0
|
||||
},
|
||||
|
||||
navBarButton: {
|
||||
iconStyle: {
|
||||
color: ColorPalette.white,
|
||||
fontSize: 24
|
||||
},
|
||||
|
||||
underlayColor: 'transparent'
|
||||
},
|
||||
|
||||
navBarContainer: {
|
||||
flexDirection: 'column',
|
||||
left: 0,
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0
|
||||
},
|
||||
|
||||
navBarSafeView: {
|
||||
left: 0,
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0
|
||||
},
|
||||
|
||||
navBarWrapper: {
|
||||
alignItems: 'center',
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
height: 44,
|
||||
justifyContent: 'space-between',
|
||||
paddingHorizontal: 14
|
||||
},
|
||||
|
||||
roomName: {
|
||||
color: ColorPalette.white,
|
||||
fontSize: 17,
|
||||
fontWeight: '400'
|
||||
},
|
||||
|
||||
roomNameWrapper: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'center',
|
||||
left: 0,
|
||||
paddingHorizontal: 48,
|
||||
position: 'absolute',
|
||||
right: 0
|
||||
},
|
||||
|
||||
/**
|
||||
* The style of the {@link View} which expands over the whole
|
||||
* {@link Conference} area and splits it between the {@link Filmstrip} and
|
||||
* the {@link Toolbox}.
|
||||
*/
|
||||
toolboxAndFilmstripContainer: {
|
||||
bottom: BoxModel.margin,
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-end',
|
||||
left: 0,
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
|
||||
// Both on Android and iOS there is the status bar which may be visible.
|
||||
// On iPhone X there is the notch. In the two cases BoxModel.margin is
|
||||
// not enough.
|
||||
top: BoxModel.margin * 3
|
||||
}
|
||||
});
|
||||
@@ -1,53 +0,0 @@
|
||||
import {
|
||||
BoxModel,
|
||||
ColorPalette,
|
||||
createStyleSheet,
|
||||
fixAndroidViewClipping
|
||||
} from '../../base/styles';
|
||||
|
||||
/**
|
||||
* The styles of the feature conference.
|
||||
*/
|
||||
export default createStyleSheet({
|
||||
/**
|
||||
* {@code Conference} style.
|
||||
*/
|
||||
conference: fixAndroidViewClipping({
|
||||
alignSelf: 'stretch',
|
||||
backgroundColor: ColorPalette.appBackground,
|
||||
flex: 1
|
||||
}),
|
||||
|
||||
displayNameBackdrop: {
|
||||
alignSelf: 'center',
|
||||
backgroundColor: 'rgba(28, 32, 37, 0.6)',
|
||||
borderRadius: 4,
|
||||
margin: 16,
|
||||
paddingHorizontal: 16,
|
||||
paddingVertical: 4
|
||||
},
|
||||
|
||||
displayNameText: {
|
||||
color: ColorPalette.white,
|
||||
fontSize: 14
|
||||
},
|
||||
|
||||
/**
|
||||
* The style of the {@link View} which expands over the whole
|
||||
* {@link Conference} area and splits it between the {@link Filmstrip} and
|
||||
* the {@link Toolbox}.
|
||||
*/
|
||||
toolboxAndFilmstripContainer: {
|
||||
bottom: BoxModel.margin,
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'flex-end',
|
||||
left: 0,
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
|
||||
// Both on Android and iOS there is the status bar which may be visible.
|
||||
// On iPhone X there is the notch. In the two cases BoxModel.margin is
|
||||
// not enough.
|
||||
top: BoxModel.margin * 3
|
||||
}
|
||||
});
|
||||
@@ -4,31 +4,33 @@ import _ from 'lodash';
|
||||
import React, { Component } from 'react';
|
||||
import { connect as reactReduxConnect } from 'react-redux';
|
||||
|
||||
import VideoLayout from '../../../../modules/UI/videolayout/VideoLayout';
|
||||
import VideoLayout from '../../../../../modules/UI/videolayout/VideoLayout';
|
||||
|
||||
import { obtainConfig } from '../../base/config';
|
||||
import { connect, disconnect } from '../../base/connection';
|
||||
import { translate } from '../../base/i18n';
|
||||
import { Chat } from '../../chat';
|
||||
import { Filmstrip } from '../../filmstrip';
|
||||
import { CalleeInfoContainer } from '../../invite';
|
||||
import { LargeVideo } from '../../large-video';
|
||||
import { NotificationsContainer } from '../../notifications';
|
||||
import { obtainConfig } from '../../../base/config';
|
||||
import { connect, disconnect } from '../../../base/connection';
|
||||
import { translate } from '../../../base/i18n';
|
||||
import { Chat } from '../../../chat';
|
||||
import { Filmstrip } from '../../../filmstrip';
|
||||
import { CalleeInfoContainer } from '../../../invite';
|
||||
import { LargeVideo } from '../../../large-video';
|
||||
import { NotificationsContainer } from '../../../notifications';
|
||||
import {
|
||||
LAYOUTS,
|
||||
getCurrentLayout,
|
||||
shouldDisplayTileView
|
||||
} from '../../video-layout';
|
||||
} from '../../../video-layout';
|
||||
|
||||
import { default as Notice } from './Notice';
|
||||
import {
|
||||
Toolbox,
|
||||
fullScreenChanged,
|
||||
setToolboxAlwaysVisible,
|
||||
showToolbox
|
||||
} from '../../toolbox';
|
||||
} from '../../../toolbox';
|
||||
|
||||
import { maybeShowSuboptimalExperienceNotification } from '../functions';
|
||||
import { maybeShowSuboptimalExperienceNotification } from '../../functions';
|
||||
|
||||
import Labels from './Labels';
|
||||
import { default as Notice } from './Notice';
|
||||
|
||||
declare var APP: Object;
|
||||
declare var config: Object;
|
||||
@@ -216,8 +218,9 @@ class Conference extends Component<Props> {
|
||||
onMouseMove = { this._onShowToolbar }>
|
||||
<Notice />
|
||||
<div id = 'videospace'>
|
||||
<LargeVideo
|
||||
hideVideoQualityLabel = { hideVideoQualityLabel } />
|
||||
<LargeVideo />
|
||||
{ hideVideoQualityLabel
|
||||
|| <Labels /> }
|
||||
<Filmstrip filmstripOnly = { filmstripOnly } />
|
||||
</div>
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { JitsiRecordingConstants } from '../../base/lib-jitsi-meet';
|
||||
import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
|
||||
|
||||
import AbstractLabels, {
|
||||
_abstractMapStateToProps as _mapStateToProps,
|
||||
type Props
|
||||
} from './AbstractLabels';
|
||||
} from '../AbstractLabels';
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} state of {@link Labels}.
|
||||
@@ -3,7 +3,7 @@
|
||||
import React, { Component } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
import { translate } from '../../../base/i18n';
|
||||
|
||||
declare var config: Object;
|
||||
|
||||
3
react/features/conference/components/web/index.js
Normal file
3
react/features/conference/components/web/index.js
Normal file
@@ -0,0 +1,3 @@
|
||||
// @flow
|
||||
|
||||
export { default as Conference } from './Conference';
|
||||
@@ -45,7 +45,7 @@ export function generateDeepLinkingURL() {
|
||||
|
||||
const appScheme = interfaceConfig.APP_SCHEME || 'org.jitsi.meet';
|
||||
const { href } = window.location;
|
||||
const regex = new RegExp(URI_PROTOCOL_PATTERN, 'i');
|
||||
const regex = new RegExp(URI_PROTOCOL_PATTERN, 'gi');
|
||||
|
||||
// Android: use an intent link, custom schemes don't work in all browsers.
|
||||
// https://developer.chrome.com/multidevice/android/intents
|
||||
|
||||
@@ -1,16 +1,3 @@
|
||||
/**
|
||||
* The type of redux action to set the {@code EventEmitter} subscriptions
|
||||
* utilized by the feature invite.
|
||||
*
|
||||
* {
|
||||
* type: _SET_EMITTER_SUBSCRIPTIONS,
|
||||
* emitterSubscriptions: Array|undefined
|
||||
* }
|
||||
*
|
||||
* @protected
|
||||
*/
|
||||
export const _SET_EMITTER_SUBSCRIPTIONS = Symbol('_SET_EMITTER_SUBSCRIPTIONS');
|
||||
|
||||
/**
|
||||
* The type of redux action which will add pending invite request to the redux
|
||||
* store.
|
||||
|
||||
@@ -38,16 +38,6 @@ type Props = AbstractButtonProps & {
|
||||
_onShareRoom: Function
|
||||
};
|
||||
|
||||
/**
|
||||
* The indicator which determines (at bundle time) whether there should be a
|
||||
* button in {@code Toolbox} to expose the functionality of the feature
|
||||
* share-room in the user interface of the app.
|
||||
*
|
||||
* @private
|
||||
* @type {boolean}
|
||||
*/
|
||||
const _SHARE_ROOM_TOOLBAR_BUTTON = true;
|
||||
|
||||
/**
|
||||
* Implements an {@link AbstractButton} to enter add/invite people to the
|
||||
* current call/conference/meeting.
|
||||
@@ -64,35 +54,12 @@ class InviteButton extends AbstractButton<Props, *> {
|
||||
* @returns {void}
|
||||
*/
|
||||
_handleClick() {
|
||||
// FIXME: dispatch _onAddPeople here, when we have a dialog for it.
|
||||
const {
|
||||
_addPeopleEnabled,
|
||||
_dialOutEnabled,
|
||||
_onAddPeople,
|
||||
_onShareRoom
|
||||
} = this.props;
|
||||
|
||||
if (_addPeopleEnabled || _dialOutEnabled) {
|
||||
_onAddPeople();
|
||||
} else if (_SHARE_ROOM_TOOLBAR_BUTTON) {
|
||||
_onShareRoom();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
* @inheritdoc
|
||||
* @returns {React$Node}
|
||||
*/
|
||||
render() {
|
||||
const { _addPeopleEnabled, _dialOutEnabled } = this.props;
|
||||
|
||||
return (
|
||||
_SHARE_ROOM_TOOLBAR_BUTTON
|
||||
|| _addPeopleEnabled
|
||||
|| _dialOutEnabled
|
||||
? super.render()
|
||||
: null);
|
||||
_onShareRoom();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +77,7 @@ class InviteButton extends AbstractButton<Props, *> {
|
||||
function _mapDispatchToProps(dispatch: Dispatch<*>) {
|
||||
return {
|
||||
/**
|
||||
* Launches native invite dialog.
|
||||
* Launches the add people dialog.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// @flow
|
||||
|
||||
import { getAppProp } from '../base/app';
|
||||
import { i18next } from '../base/i18n';
|
||||
import { isLocalParticipantModerator } from '../base/participants';
|
||||
import { doGetJSON, parseURIString } from '../base/util';
|
||||
@@ -297,18 +296,7 @@ export function invitePeopleAndChatRooms( // eslint-disable-line max-params
|
||||
export function isAddPeopleEnabled(state: Object): boolean {
|
||||
const { isGuest } = state['features/base/jwt'];
|
||||
|
||||
if (!isGuest) {
|
||||
// XXX The mobile/react-native app is capable of disabling the
|
||||
// adding/inviting of people in the current conference. Anyway, the
|
||||
// Web/React app does not have that capability so default appropriately.
|
||||
const addPeopleEnabled = getAppProp(state, 'addPeopleEnabled');
|
||||
|
||||
return (
|
||||
(typeof addPeopleEnabled === 'undefined')
|
||||
|| Boolean(addPeopleEnabled));
|
||||
}
|
||||
|
||||
return false;
|
||||
return !isGuest;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -319,21 +307,9 @@ export function isAddPeopleEnabled(state: Object): boolean {
|
||||
*/
|
||||
export function isDialOutEnabled(state: Object): boolean {
|
||||
const { conference } = state['features/base/conference'];
|
||||
let dialOutEnabled = isLocalParticipantModerator(state)
|
||||
&& conference
|
||||
&& conference.isSIPCallingSupported();
|
||||
|
||||
if (dialOutEnabled) {
|
||||
// XXX The mobile/react-native app is capable of disabling of dial-out.
|
||||
// Anyway, the Web/React app does not have that capability so default
|
||||
// appropriately.
|
||||
dialOutEnabled = getAppProp(state, 'dialOutEnabled');
|
||||
|
||||
return (
|
||||
(typeof dialOutEnabled === 'undefined') || Boolean(dialOutEnabled));
|
||||
}
|
||||
|
||||
return false;
|
||||
return isLocalParticipantModerator(state)
|
||||
&& conference && conference.isSIPCallingSupported();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,232 +0,0 @@
|
||||
// @flow
|
||||
|
||||
import i18next from 'i18next';
|
||||
import { NativeEventEmitter, NativeModules } from 'react-native';
|
||||
|
||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT, getAppProp } from '../base/app';
|
||||
import { MiddlewareRegistry } from '../base/redux';
|
||||
|
||||
import { invite } from './actions';
|
||||
import {
|
||||
BEGIN_ADD_PEOPLE,
|
||||
_SET_EMITTER_SUBSCRIPTIONS
|
||||
} from './actionTypes';
|
||||
import {
|
||||
getInviteResultsForQuery,
|
||||
isAddPeopleEnabled,
|
||||
isDialOutEnabled
|
||||
} from './functions';
|
||||
import './middleware.any';
|
||||
|
||||
/**
|
||||
* The react-native module of the feature invite.
|
||||
*/
|
||||
const { Invite } = NativeModules;
|
||||
|
||||
/**
|
||||
* The middleware of the feature invite specific to mobile/react-native.
|
||||
*
|
||||
* @param {Store} store - The redux store.
|
||||
* @returns {Function}
|
||||
*/
|
||||
Invite && MiddlewareRegistry.register(store => next => action => {
|
||||
switch (action.type) {
|
||||
case _SET_EMITTER_SUBSCRIPTIONS:
|
||||
return _setEmitterSubscriptions(store, next, action);
|
||||
|
||||
case APP_WILL_MOUNT:
|
||||
return _appWillMount(store, next, action);
|
||||
|
||||
case APP_WILL_UNMOUNT: {
|
||||
const result = next(action);
|
||||
|
||||
store.dispatch({
|
||||
type: _SET_EMITTER_SUBSCRIPTIONS,
|
||||
emitterSubscriptions: undefined
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
case BEGIN_ADD_PEOPLE:
|
||||
return _beginAddPeople(store, next, action);
|
||||
}
|
||||
|
||||
return next(action);
|
||||
});
|
||||
|
||||
/**
|
||||
* Notifies the feature jwt that the action {@link APP_WILL_MOUNT} is being
|
||||
* dispatched within a specific redux {@code store}.
|
||||
*
|
||||
* @param {Store} store - The redux store in which the specified {@code action}
|
||||
* is being dispatched.
|
||||
* @param {Dispatch} next - The redux dispatch function to dispatch the
|
||||
* specified {@code action} to the specified {@code store}.
|
||||
* @param {Action} action - The redux action {@code APP_WILL_MOUNT} which is
|
||||
* being dispatched in the specified {@code store}.
|
||||
* @private
|
||||
* @returns {*} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _appWillMount({ dispatch, getState }, next, action) {
|
||||
const result = next(action);
|
||||
|
||||
const emitter = new NativeEventEmitter(Invite);
|
||||
const context = {
|
||||
dispatch,
|
||||
getState
|
||||
};
|
||||
|
||||
dispatch({
|
||||
type: _SET_EMITTER_SUBSCRIPTIONS,
|
||||
emitterSubscriptions: [
|
||||
emitter.addListener(
|
||||
'org.jitsi.meet:features/invite#invite',
|
||||
_onInvite,
|
||||
context),
|
||||
emitter.addListener(
|
||||
'org.jitsi.meet:features/invite#performQuery',
|
||||
_onPerformQuery,
|
||||
context)
|
||||
]
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the feature invite that the action {@link BEGIN_ADD_PEOPLE} is being
|
||||
* dispatched within a specific redux {@code store}.
|
||||
*
|
||||
* @param {Store} store - The redux store in which the specified {@code action}
|
||||
* is being dispatched.
|
||||
* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
|
||||
* specified {@code action} to the specified {@code store}.
|
||||
* @param {Action} action - The redux action {@code BEGIN_ADD_PEOPLE} which is
|
||||
* being dispatched in the specified {@code store}.
|
||||
* @private
|
||||
* @returns {*} The value returned by {@code next(action)}.
|
||||
*/
|
||||
function _beginAddPeople(store, next, action) {
|
||||
const result = next(action);
|
||||
|
||||
// The JavaScript App needs to provide uniquely identifying information to
|
||||
// the native Invite module so that the latter may match the former to the
|
||||
// native JitsiMeetView which hosts it.
|
||||
const externalAPIScope = getAppProp(store, 'externalAPIScope');
|
||||
|
||||
externalAPIScope && Invite.beginAddPeople(externalAPIScope);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the {@code invite} event of the feature invite and invites specific
|
||||
* invitees to the current, ongoing conference.
|
||||
*
|
||||
* @param {Object} event - The details of the event.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _onInvite({ addPeopleControllerScope, externalAPIScope, invitees }) {
|
||||
const { dispatch, getState } = this; // eslint-disable-line no-invalid-this
|
||||
|
||||
// If there are multiple JitsiMeetView instances alive, they will all get
|
||||
// the event, since there is a single bridge, so make sure we don't act if
|
||||
// the event is not for us.
|
||||
if (getAppProp(getState, 'externalAPIScope') !== externalAPIScope) {
|
||||
return;
|
||||
}
|
||||
|
||||
dispatch(invite(invitees))
|
||||
.then(failedInvitees =>
|
||||
Invite.inviteSettled(
|
||||
externalAPIScope,
|
||||
addPeopleControllerScope,
|
||||
failedInvitees));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the {@code performQuery} event of the feature invite and queries for
|
||||
* invitees who may subsequently be invited to the current, ongoing conference.
|
||||
*
|
||||
* @param {Object} event - The details of the event.
|
||||
* @returns {void}
|
||||
*/
|
||||
function _onPerformQuery(
|
||||
{ addPeopleControllerScope, externalAPIScope, query }) {
|
||||
const { getState } = this; // eslint-disable-line no-invalid-this
|
||||
const state = getState();
|
||||
|
||||
// If there are multiple JitsiMeetView instances alive, they will all get
|
||||
// the event, since there is a single bridge, so make sure we don't act if
|
||||
// the event is not for us.
|
||||
if (getAppProp(state, 'externalAPIScope') !== externalAPIScope) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
dialOutAuthUrl,
|
||||
peopleSearchQueryTypes,
|
||||
peopleSearchUrl
|
||||
} = state['features/base/config'];
|
||||
const options = {
|
||||
dialOutAuthUrl,
|
||||
addPeopleEnabled: isAddPeopleEnabled(state),
|
||||
dialOutEnabled: isDialOutEnabled(state),
|
||||
jwt: state['features/base/jwt'].jwt,
|
||||
peopleSearchQueryTypes,
|
||||
peopleSearchUrl
|
||||
};
|
||||
|
||||
getInviteResultsForQuery(query, options)
|
||||
.catch(() => [])
|
||||
.then(results => {
|
||||
const translatedResults = results.map(result => {
|
||||
if (result.type === 'phone') {
|
||||
result.title = i18next.t('addPeople.telephone', {
|
||||
number: result.number
|
||||
});
|
||||
|
||||
if (result.showCountryCodeReminder) {
|
||||
result.subtitle = i18next.t(
|
||||
'addPeople.countryReminder'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}).filter(result => result.type !== 'phone' || result.allowed);
|
||||
|
||||
Invite.receivedResults(
|
||||
externalAPIScope,
|
||||
addPeopleControllerScope,
|
||||
query,
|
||||
translatedResults);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies the feature invite that the action
|
||||
* {@link _SET_EMITTER_SUBSCRIPTIONS} is being dispatched within a specific
|
||||
* redux {@code store}.
|
||||
*
|
||||
* @param {Store} store - The redux store in which the specified {@code action}
|
||||
* is being dispatched.
|
||||
* @param {Dispatch} next - The redux dispatch function to dispatch the
|
||||
* specified {@code action} to the specified {@code store}.
|
||||
* @param {Action} action - The redux action {@code _SET_EMITTER_SUBSCRIPTIONS}
|
||||
* which is being dispatched in the specified {@code store}.
|
||||
* @private
|
||||
* @returns {*}
|
||||
*/
|
||||
function _setEmitterSubscriptions({ getState }, next, action) {
|
||||
const { emitterSubscriptions } = getState()['features/invite'];
|
||||
|
||||
if (emitterSubscriptions) {
|
||||
for (const subscription of emitterSubscriptions) {
|
||||
subscription.remove();
|
||||
}
|
||||
}
|
||||
|
||||
return next(action);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
// @flow
|
||||
|
||||
import { assign, ReducerRegistry } from '../base/redux';
|
||||
import { ReducerRegistry } from '../base/redux';
|
||||
|
||||
import {
|
||||
_SET_EMITTER_SUBSCRIPTIONS,
|
||||
ADD_PENDING_INVITE_REQUEST,
|
||||
REMOVE_PENDING_INVITE_REQUESTS,
|
||||
SET_CALLEE_INFO_VISIBLE,
|
||||
@@ -26,9 +25,6 @@ const DEFAULT_STATE = {
|
||||
|
||||
ReducerRegistry.register('features/invite', (state = DEFAULT_STATE, action) => {
|
||||
switch (action.type) {
|
||||
case _SET_EMITTER_SUBSCRIPTIONS:
|
||||
return (
|
||||
assign(state, 'emitterSubscriptions', action.emitterSubscriptions));
|
||||
case ADD_PENDING_INVITE_REQUEST:
|
||||
return {
|
||||
...state,
|
||||
@@ -37,6 +33,7 @@ ReducerRegistry.register('features/invite', (state = DEFAULT_STATE, action) => {
|
||||
action.request
|
||||
]
|
||||
};
|
||||
|
||||
case REMOVE_PENDING_INVITE_REQUESTS:
|
||||
return {
|
||||
...state,
|
||||
|
||||
@@ -6,7 +6,6 @@ import { connect } from 'react-redux';
|
||||
import { ParticipantView } from '../../base/participants';
|
||||
import { DimensionsDetector } from '../../base/responsive-ui';
|
||||
|
||||
import Labels from './Labels';
|
||||
import styles, { AVATAR_SIZE } from './styles';
|
||||
|
||||
/**
|
||||
@@ -130,7 +129,6 @@ class LargeVideo extends Component<Props, State> {
|
||||
useConnectivityInfoLabel = { useConnectivityInfoLabel }
|
||||
zOrder = { 0 }
|
||||
zoomEnabled = { true } />
|
||||
<Labels />
|
||||
</DimensionsDetector>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,28 +5,15 @@ import React, { Component } from 'react';
|
||||
import { Watermarks } from '../../base/react';
|
||||
import { Captions } from '../../subtitles/';
|
||||
|
||||
import Labels from './Labels';
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
/**
|
||||
* The type of the React {@code Component} props of {@link LargeVideo}.
|
||||
*/
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* True if the {@code VideoQualityLabel} should not be displayed.
|
||||
*/
|
||||
hideVideoQualityLabel: boolean
|
||||
};
|
||||
|
||||
/**
|
||||
* Implements a React {@link Component} which represents the large video (a.k.a.
|
||||
* the conference participant who is on the local stage) on Web/React.
|
||||
*
|
||||
* @extends Component
|
||||
*/
|
||||
export default class LargeVideo extends Component<Props> {
|
||||
export default class LargeVideo extends Component<{}> {
|
||||
/**
|
||||
* Implements React's {@link Component#render()}.
|
||||
*
|
||||
@@ -47,9 +34,7 @@ export default class LargeVideo extends Component<Props> {
|
||||
|
||||
<div id = 'dominantSpeaker'>
|
||||
<div className = 'dynamic-shadow' />
|
||||
<img
|
||||
id = 'dominantSpeakerAvatar'
|
||||
src = '' />
|
||||
<div id = 'dominantSpeakerAvatarContainer' />
|
||||
</div>
|
||||
<div id = 'remotePresenceMessage' />
|
||||
<span id = 'remoteConnectionMessage' />
|
||||
@@ -74,8 +59,6 @@ export default class LargeVideo extends Component<Props> {
|
||||
{ interfaceConfig.DISABLE_TRANSCRIPTION_SUBTITLES
|
||||
|| <Captions /> }
|
||||
<span id = 'localConnectionMessage' />
|
||||
{ this.props.hideVideoQualityLabel
|
||||
|| <Labels /> }
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { StyleSheet } from 'react-native';
|
||||
|
||||
import { BoxModel, ColorPalette, createStyleSheet } from '../../base/styles';
|
||||
import { FILMSTRIP_SIZE } from '../../filmstrip';
|
||||
import { ColorPalette, createStyleSheet } from '../../base/styles';
|
||||
|
||||
/**
|
||||
* Size for the Avatar.
|
||||
@@ -10,34 +9,6 @@ export const AVATAR_SIZE = 200;
|
||||
|
||||
export default createStyleSheet({
|
||||
|
||||
/**
|
||||
* View that contains the indicators.
|
||||
*/
|
||||
indicatorContainer: {
|
||||
flex: 1,
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'flex-end',
|
||||
margin: BoxModel.margin
|
||||
},
|
||||
|
||||
/**
|
||||
* Indicator container for wide aspect ratio.
|
||||
*/
|
||||
indicatorContainerWide: {
|
||||
marginRight: FILMSTRIP_SIZE + BoxModel.margin
|
||||
},
|
||||
|
||||
labelWrapper: {
|
||||
flexDirection: 'column',
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
|
||||
// Both on Android and iOS there is the status bar which may be visible.
|
||||
// On iPhone X there is the notch. In the two cases BoxModel.margin is
|
||||
// not enough.
|
||||
top: BoxModel.margin * 3
|
||||
},
|
||||
|
||||
/**
|
||||
* Large video container style.
|
||||
*/
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
CONFERENCE_WILL_JOIN,
|
||||
CONFERENCE_JOINED,
|
||||
SET_AUDIO_ONLY,
|
||||
getConferenceName,
|
||||
getCurrentConference
|
||||
} from '../../base/conference';
|
||||
import { getInviteURL } from '../../base/connection';
|
||||
@@ -226,12 +227,7 @@ function _conferenceWillJoin({ dispatch, getState }, next, action) {
|
||||
|
||||
CallIntegration.startCall(conference.callUUID, handle, hasVideo)
|
||||
.then(() => {
|
||||
const { callee } = state['features/base/jwt'];
|
||||
const displayName
|
||||
= state['features/base/config'].callDisplayName
|
||||
|| (callee && callee.name)
|
||||
|| state['features/base/conference'].room;
|
||||
|
||||
const displayName = getConferenceName(state);
|
||||
const muted
|
||||
= isLocalTrackMuted(
|
||||
state['features/base/tracks'],
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// @flow
|
||||
|
||||
import { StatusBar } from 'react-native';
|
||||
import { Immersive } from 'react-native-immersive';
|
||||
|
||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from '../../base/app';
|
||||
@@ -91,10 +90,6 @@ function _setFullScreen(fullScreen: boolean) {
|
||||
// throws on other platforms.
|
||||
if (Platform.OS === 'android') {
|
||||
fullScreen ? Immersive.on() : Immersive.off();
|
||||
} else {
|
||||
// On platforms other than Android go with whatever React Native itself
|
||||
// supports.
|
||||
StatusBar.setHidden(fullScreen, 'slide');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,6 @@ import {
|
||||
} from '../../../base/dialog';
|
||||
import { InviteButton } from '../../../invite';
|
||||
import { AudioRouteButton } from '../../../mobile/audio-mode';
|
||||
import { PictureInPictureButton } from '../../../mobile/picture-in-picture';
|
||||
import { LiveStreamButton, RecordButton } from '../../../recording';
|
||||
import { RoomLockButton } from '../../../room-lock';
|
||||
import { ClosedCaptionButton } from '../../../subtitles';
|
||||
@@ -90,7 +89,6 @@ class OverflowMenu extends Component<Props> {
|
||||
<LiveStreamButton { ...buttonProps } />
|
||||
<TileViewButton { ...buttonProps } />
|
||||
<InviteButton { ...buttonProps } />
|
||||
<PictureInPictureButton { ...buttonProps } />
|
||||
</BottomSheet>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,12 @@ import { connect } from 'react-redux';
|
||||
import { Container } from '../../../base/react';
|
||||
import { ChatButton } from '../../../chat';
|
||||
|
||||
import { isToolboxVisible } from '../../functions';
|
||||
|
||||
import AudioMuteButton from '../AudioMuteButton';
|
||||
import HangupButton from '../HangupButton';
|
||||
import VideoMuteButton from '../VideoMuteButton';
|
||||
|
||||
import OverflowMenuButton from './OverflowMenuButton';
|
||||
import styles, {
|
||||
chatButtonOverride,
|
||||
@@ -16,7 +20,6 @@ import styles, {
|
||||
toolbarButtonStyles,
|
||||
toolbarToggledButtonStyles
|
||||
} from './styles';
|
||||
import VideoMuteButton from '../VideoMuteButton';
|
||||
|
||||
/**
|
||||
* The number of buttons other than {@link HangupButton} to render in
|
||||
@@ -262,10 +265,8 @@ class Toolbox extends Component<Props, State> {
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state: Object): Object {
|
||||
const { alwaysVisible, enabled, visible } = state['features/toolbox'];
|
||||
|
||||
return {
|
||||
_visible: enabled && (alwaysVisible || visible)
|
||||
_visible: isToolboxVisible(state)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
17
react/features/toolbox/functions.any.js
Normal file
17
react/features/toolbox/functions.any.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// @flow
|
||||
|
||||
import { toState } from '../base/redux';
|
||||
|
||||
/**
|
||||
* Returns true if the toolbox is visible.
|
||||
*
|
||||
* @param {Object | Function} stateful - A function or object that can be
|
||||
* resolved to Redux state by the function {@code toState}.
|
||||
* @returns {boolean}
|
||||
*/
|
||||
export function isToolboxVisible(stateful: Object | Function) {
|
||||
const { alwaysVisible, enabled, visible }
|
||||
= toState(stateful)['features/toolbox'];
|
||||
|
||||
return enabled && (alwaysVisible || visible);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
// @flow
|
||||
|
||||
export * from './functions.any';
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// @flow
|
||||
|
||||
export * from './functions.any';
|
||||
|
||||
declare var interfaceConfig: Object;
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user