[RN] Add an example how to consume the public SDK invite API

This commit is contained in:
Lyubo Marinov 2018-05-06 22:39:20 -05:00
parent 85612b9ae1
commit 7ffdaf59c7
8 changed files with 253 additions and 58 deletions

View File

@ -23,11 +23,15 @@ 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.calendarevents.CalendarEventsPackage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@ -43,6 +47,14 @@ 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() {
JitsiMeetView view = super.initializeView();
@ -93,23 +105,68 @@ public class MainActivity extends JitsiMeetActivity {
}
});
view.getInviteController().setListener(
new InviteControllerListener() {
// inviteController
final InviteController inviteController
= view.getInviteController();
inviteController.setListener(new InviteControllerListener() {
public void beginAddPeople(
AddPeopleController addPeopleController) {
// 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");
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) {
// 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) {
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
@ -122,9 +179,61 @@ public class MainActivity extends JitsiMeetActivity {
super.onCreate(savedInstanceState);
}
private void onInviteControllerBeginAddPeople(
InviteController inviteController,
AddPeopleController addPeopleController) {
// 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();
}
}
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
CalendarEventsPackage.onRequestPermissionsResult(requestCode, permissions, grantResults);
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
public void onRequestPermissionsResult(
int requestCode,
String[] permissions,
int[] grantResults) {
CalendarEventsPackage.onRequestPermissionsResult(
requestCode,
permissions,
grantResults);
super.onRequestPermissionsResult(
requestCode,
permissions,
grantResults);
}
}

View File

@ -122,6 +122,11 @@ public class JitsiMeetActivity extends AppCompatActivity {
JitsiMeetView view = initializeView();
if (view != null) {
// XXX Allow extenders who override initializeView() to configure
// the view before the first loadURL(). Probably works around a
// problem related to ReactRootView#setAppProperties().
view.loadURL(null);
this.view = view;
setContentView(this.view);
}
@ -144,8 +149,6 @@ public class JitsiMeetActivity extends AppCompatActivity {
}
view.setWelcomePageEnabled(welcomePageEnabled);
view.loadURL(null);
return view;
}

View File

@ -116,7 +116,7 @@ public class AddPeopleController {
if(items.containsKey(id)) {
WritableNativeMap map = new WritableNativeMap();
map.merge(items.get(ids));
map.merge(items.get(id));
invitees.pushMap(map);
} else {
// If the id doesn't exist in the map, we can't do anything,
@ -138,14 +138,14 @@ public class AddPeopleController {
jFailedInvitees.add(failedInvitees.getMap(i).toHashMap());
}
listener.inviteSettled(this, jFailedInvitees);
listener.onInviteSettled(this, jFailedInvitees);
}
}
/**
* Start a search for entities to invite with the given query. Results will
* be returned through the associated AddPeopleControllerListener's
* onReceiveResults method.
* onReceivedResults method.
*
* @param query
*/
@ -188,7 +188,7 @@ public class AddPeopleController {
jvmResults.add(map.toHashMap());
}
listener.onReceiveResults(this, jvmResults, query);
listener.onReceivedResults(this, jvmResults, query);
}
}

View File

@ -21,29 +21,36 @@ import java.util.Map;
public interface AddPeopleControllerListener {
/**
* Called when results are received for a query called through AddPeopleController.query()
* Called when the call to {@link AddPeopleController#inviteById(List)}
* completes.
*
* @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
* @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 onReceiveResults(AddPeopleController addPeopleController, List<Map<String, Object>> results, String query);
void onInviteSettled(
AddPeopleController addPeopleController,
List<Map<String, Object>> failedInvitees);
/**
* Called when the call to {@link AddPeopleController#inviteById(List)} completes, but the
* invitation fails for one or more of the selected items.
* Called when results are received for a query called through
* AddPeopleController.query().
*
* @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 onReceiveResuls.
* @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 inviteSettled(AddPeopleController addPeopleController, List<Map<String, Object>> failedInvitees);
void onReceivedResults(
AddPeopleController addPeopleController,
List<Map<String, Object>> results,
String query);
}

View File

@ -61,10 +61,6 @@ public class InviteController {
this.externalAPIScope = externalAPIScope;
}
public InviteControllerListener getListener() {
return listener;
}
void beginAddPeople(ReactApplicationContext reactContext) {
InviteControllerListener listener = getListener();
@ -102,6 +98,10 @@ public class InviteController {
}
}
public InviteControllerListener getListener() {
return listener;
}
/**
* Sends JavaScript event to submit invitations to the given item ids
*
@ -203,7 +203,7 @@ public class InviteController {
/**
* Starts a query for users to invite to the conference. Results will be
* returned through the {@link AddPeopleControllerListener#onReceiveResults(AddPeopleController, List, String)}
* returned through the {@link AddPeopleControllerListener#onReceivedResults(AddPeopleController, List, String)}
* method.
*
* @param query {@code String} to use for the query
@ -211,8 +211,8 @@ public class InviteController {
void performQuery(AddPeopleController addPeopleController, String query) {
WritableNativeMap params = new WritableNativeMap();
params.putString("externalAPIScope", externalAPIScope);
params.putString("addPeopleControllerScope", addPeopleController.getUuid());
params.putString("externalAPIScope", externalAPIScope);
params.putString("query", query);
ReactContextUtils.emitEvent(
addPeopleController.getReactApplicationContext(),

View File

@ -18,6 +18,10 @@
#import <JitsiMeet/JitsiMeet.h>
@interface ViewController : UIViewController<JitsiMeetViewDelegate, JMInviteControllerDelegate>
@interface ViewController
: UIViewController<
JitsiMeetViewDelegate,
JMAddPeopleControllerDelegate,
JMInviteControllerDelegate>
@end

View File

@ -16,6 +16,13 @@
#import "ViewController.h"
/**
* 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
@ -31,7 +38,12 @@
view.delegate = self;
view.inviteController.delegate = self;
// inviteController
JMInviteController *inviteController = view.inviteController;
inviteController.delegate = self;
inviteController.addPeopleEnabled
= inviteController.dialOutEnabled
= ADD_PEOPLE_CONTROLLER_QUERY != nil;
#endif // #ifdef DEBUG
@ -46,6 +58,8 @@
#if DEBUG
// JitsiMeetViewDelegate
void _onJitsiMeetViewDelegateEvent(NSString *name, NSDictionary *data) {
NSLog(
@"[%s:%d] JitsiMeetViewDelegate %@ %@",
@ -76,14 +90,72 @@ void _onJitsiMeetViewDelegateEvent(NSString *name, NSDictionary *data) {
_onJitsiMeetViewDelegateEvent(@"LOAD_CONFIG_ERROR", data);
}
// JMInviteControllerDelegate
- (void)beginAddPeople:(JMAddPeopleController *)addPeopleController {
NSLog(
@"[%s:%d] JMInviteControllerDelegate %s",
__FILE__, __LINE__, __FUNCTION__);
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 {
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 {
// 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];
}

View File

@ -70,15 +70,6 @@ class InviteButton extends Component<Props> {
...props
} = this.props;
if (_SHARE_ROOM_TOOLBAR_BUTTON) {
return (
<ToolbarButton
iconName = 'link'
onClick = { _onShareRoom }
{ ...props } />
);
}
if (_addPeopleEnabled || _dialOutEnabled) {
return (
<ToolbarButton
@ -88,6 +79,15 @@ class InviteButton extends Component<Props> {
);
}
if (_SHARE_ROOM_TOOLBAR_BUTTON) {
return (
<ToolbarButton
iconName = 'link'
onClick = { _onShareRoom }
{ ...props } />
);
}
return null;
}
}