ios: introduce JitsiMeetConferenceOptions
This commit is contained in:
parent
b97cb3509a
commit
aedcfba263
|
@ -37,11 +37,18 @@
|
|||
[Fabric with:@[[Crashlytics class]]];
|
||||
}
|
||||
|
||||
[JitsiMeet sharedInstance].conferenceActivityType = JitsiMeetConferenceActivityType;
|
||||
[JitsiMeet sharedInstance].customUrlScheme = @"org.jitsi.meet";
|
||||
[JitsiMeet sharedInstance].universalLinkDomains = @[@"meet.jit.si", @"beta.meet.jit.si"];
|
||||
JitsiMeet *jitsiMeet = [JitsiMeet sharedInstance];
|
||||
|
||||
[[JitsiMeet sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
jitsiMeet.conferenceActivityType = JitsiMeetConferenceActivityType;
|
||||
jitsiMeet.customUrlScheme = @"org.jitsi.meet";
|
||||
jitsiMeet.universalLinkDomains = @[@"meet.jit.si", @"beta.meet.jit.si"];
|
||||
|
||||
jitsiMeet.defaultConferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
|
||||
builder.serverURL = [NSURL URLWithString:@"https://meet.jit.si"];
|
||||
builder.welcomePageEnabled = YES;
|
||||
}];
|
||||
|
||||
[jitsiMeet application:application didFinishLaunchingWithOptions:launchOptions];
|
||||
|
||||
return YES;
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
@import MobileCoreServices;
|
||||
@import Intents; // Needed for NSUserActivity suggestedInvocationPhrase
|
||||
|
||||
@import JitsiMeet;
|
||||
|
||||
#import "Types.h"
|
||||
#import "ViewController.h"
|
||||
|
||||
|
@ -33,13 +35,7 @@
|
|||
JitsiMeetView *view = (JitsiMeetView *) self.view;
|
||||
view.delegate = self;
|
||||
|
||||
// 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
|
||||
// anyway.
|
||||
view.welcomePageEnabled = YES;
|
||||
|
||||
[view join:[[JitsiMeet sharedInstance] getInitialURL]];
|
||||
[view join:[[JitsiMeet sharedInstance] getInitialConferenceOptions]];
|
||||
}
|
||||
|
||||
// JitsiMeetViewDelegate
|
||||
|
|
|
@ -42,7 +42,8 @@
|
|||
C69EFA0E209A0F660027712B /* JMCallKitListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69EFA0B209A0F660027712B /* JMCallKitListener.swift */; };
|
||||
C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425E204EF76800E062DD /* DragGestureController.swift */; };
|
||||
C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */; };
|
||||
C6F99C15204DB63E0001F710 /* JitsiMeetView+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */; };
|
||||
DEAD3226220C497000E93636 /* JitsiMeetConferenceOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = DEAD3224220C497000E93636 /* JitsiMeetConferenceOptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
DEAD3227220C497000E93636 /* JitsiMeetConferenceOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = DEAD3225220C497000E93636 /* JitsiMeetConferenceOptions.m */; };
|
||||
DEFC743F21B178FA00E4DD96 /* LocaleDetector.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */; };
|
||||
DEFE535421FB1BF800011A3A /* JitsiMeet.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFE535321FB1BF800011A3A /* JitsiMeet.m */; };
|
||||
DEFE535621FB2E8300011A3A /* ReactUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFE535521FB2E8300011A3A /* ReactUtils.m */; };
|
||||
|
@ -92,6 +93,9 @@
|
|||
C6A3425E204EF76800E062DD /* DragGestureController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DragGestureController.swift; sourceTree = "<group>"; };
|
||||
C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiPViewCoordinator.swift; sourceTree = "<group>"; };
|
||||
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
|
||||
DEAD3224220C497000E93636 /* JitsiMeetConferenceOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetConferenceOptions.h; sourceTree = "<group>"; };
|
||||
DEAD3225220C497000E93636 /* JitsiMeetConferenceOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeetConferenceOptions.m; sourceTree = "<group>"; };
|
||||
DEAD3228220C734300E93636 /* JitsiMeetConferenceOptions+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetConferenceOptions+Private.h"; sourceTree = "<group>"; };
|
||||
DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LocaleDetector.m; sourceTree = "<group>"; };
|
||||
DEFE535321FB1BF800011A3A /* JitsiMeet.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeet.m; sourceTree = "<group>"; };
|
||||
DEFE535521FB2E8300011A3A /* ReactUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactUtils.m; sourceTree = "<group>"; };
|
||||
|
@ -164,6 +168,9 @@
|
|||
0BD906E81EC0C00300C8C18E /* JitsiMeet.h */,
|
||||
DEFE535821FB311F00011A3A /* JitsiMeet+Private.h */,
|
||||
DEFE535321FB1BF800011A3A /* JitsiMeet.m */,
|
||||
DEAD3224220C497000E93636 /* JitsiMeetConferenceOptions.h */,
|
||||
DEAD3228220C734300E93636 /* JitsiMeetConferenceOptions+Private.h */,
|
||||
DEAD3225220C497000E93636 /* JitsiMeetConferenceOptions.m */,
|
||||
0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
|
||||
0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
|
||||
C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */,
|
||||
|
@ -247,11 +254,11 @@
|
|||
isa = PBXHeadersBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
C6F99C15204DB63E0001F710 /* JitsiMeetView+Private.h in Headers */,
|
||||
0B412F181EDEC65D00B1A0A6 /* JitsiMeetView.h in Headers */,
|
||||
0B93EF7E1EC9DDCD0030D24D /* RCTBridgeWrapper.h in Headers */,
|
||||
0B412F221EDEF6EA00B1A0A6 /* JitsiMeetViewDelegate.h in Headers */,
|
||||
0BD906EA1EC0C00300C8C18E /* JitsiMeet.h in Headers */,
|
||||
DEAD3226220C497000E93636 /* JitsiMeetConferenceOptions.h in Headers */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
@ -451,6 +458,7 @@
|
|||
files = (
|
||||
0BB9AD7B1F5EC8F4001C08DB /* CallKit.m in Sources */,
|
||||
0BB9AD7D1F60356D001C08DB /* AppInfo.m in Sources */,
|
||||
DEAD3227220C497000E93636 /* JitsiMeetConferenceOptions.m in Sources */,
|
||||
0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
|
||||
0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
|
||||
0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */,
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
@interface JitsiMeet ()
|
||||
|
||||
- (NSDictionary *)getDefaultProps;
|
||||
- (RCTBridge *)getReactBridge;
|
||||
|
||||
@end
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
|
||||
#import <JitsiMeet/JitsiMeetView.h>
|
||||
#import <JitsiMeet/JitsiMeetViewDelegate.h>
|
||||
#import <JitsiMeet/JitsiMeetConferenceOptions.h>
|
||||
|
||||
|
||||
@interface JitsiMeet : NSObject
|
||||
|
@ -25,6 +26,8 @@
|
|||
@property (copy, nonatomic, nullable) NSString *customUrlScheme;
|
||||
@property (copy, nonatomic, nullable) NSArray<NSString *> *universalLinkDomains;
|
||||
|
||||
@property (nonatomic, nullable) JitsiMeetConferenceOptions *defaultConferenceOptions;
|
||||
|
||||
#pragma mak - This class is a singleton
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
@ -44,6 +47,6 @@
|
|||
|
||||
#pragma mark - Utility methods
|
||||
|
||||
- (NSDictionary *)getInitialURL;
|
||||
- (JitsiMeetConferenceOptions *)getInitialConferenceOptions;
|
||||
|
||||
@end
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
|
||||
#import "Dropbox.h"
|
||||
#import "JitsiMeet+Private.h"
|
||||
#import "JitsiMeetConferenceOptions+Private.h"
|
||||
#import "JitsiMeetView+Private.h"
|
||||
#import "RCTBridgeWrapper.h"
|
||||
#import "ReactUtils.h"
|
||||
|
@ -71,9 +72,9 @@
|
|||
continueUserActivity:(NSUserActivity *)userActivity
|
||||
restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {
|
||||
|
||||
id url = [self urlFromUserActivity:userActivity];
|
||||
JitsiMeetConferenceOptions *options = [self optionsFromUserActivity:userActivity];
|
||||
|
||||
return url && [JitsiMeetView loadURLInViews:url];
|
||||
return options && [JitsiMeetView setPropsInViews:[options asProps]];
|
||||
}
|
||||
|
||||
- (BOOL)application:(UIApplication *)app
|
||||
|
@ -95,36 +96,44 @@
|
|||
return NO;
|
||||
}
|
||||
|
||||
return [JitsiMeetView loadURLInViews:@{ @"url" : url.absoluteString }];
|
||||
JitsiMeetConferenceOptions *conferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
|
||||
builder.room = [url absoluteString];
|
||||
}];
|
||||
|
||||
return [JitsiMeetView setPropsInViews:[conferenceOptions asProps]];
|
||||
}
|
||||
|
||||
#pragma mark - Utility methods
|
||||
|
||||
- (NSDictionary *)getInitialURL {
|
||||
- (JitsiMeetConferenceOptions *)getInitialConferenceOptions {
|
||||
if (_launchOptions[UIApplicationLaunchOptionsURLKey]) {
|
||||
NSURL *url = _launchOptions[UIApplicationLaunchOptionsURLKey];
|
||||
return @{ @"url" : url.absoluteString };
|
||||
return [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
|
||||
builder.room = [url absoluteString];
|
||||
}];
|
||||
} else {
|
||||
NSDictionary *userActivityDictionary
|
||||
= _launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey];
|
||||
NSUserActivity *userActivity
|
||||
= [userActivityDictionary objectForKey:@"UIApplicationLaunchOptionsUserActivityKey"];
|
||||
if (userActivity != nil) {
|
||||
return [self urlFromUserActivity:userActivity];
|
||||
return [self optionsFromUserActivity:userActivity];
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSDictionary *)urlFromUserActivity:(NSUserActivity *)userActivity {
|
||||
- (JitsiMeetConferenceOptions *)optionsFromUserActivity:(NSUserActivity *)userActivity {
|
||||
NSString *activityType = userActivity.activityType;
|
||||
|
||||
if ([activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
|
||||
// App was started by opening a URL in the browser
|
||||
NSURL *url = userActivity.webpageURL;
|
||||
if ([_universalLinkDomains containsObject:url.host]) {
|
||||
return @{ @"url" : url.absoluteString };
|
||||
return [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
|
||||
builder.room = [url absoluteString];
|
||||
}];
|
||||
}
|
||||
} else if ([activityType isEqualToString:@"INStartAudioCallIntent"]
|
||||
|| [activityType isEqualToString:@"INStartVideoCallIntent"]) {
|
||||
|
@ -132,27 +141,29 @@
|
|||
INIntent *intent = userActivity.interaction.intent;
|
||||
NSArray<INPerson *> *contacts;
|
||||
NSString *url;
|
||||
BOOL startAudioOnly = NO;
|
||||
BOOL audioOnly = NO;
|
||||
|
||||
if ([intent isKindOfClass:[INStartAudioCallIntent class]]) {
|
||||
contacts = ((INStartAudioCallIntent *) intent).contacts;
|
||||
startAudioOnly = YES;
|
||||
audioOnly = YES;
|
||||
} else if ([intent isKindOfClass:[INStartVideoCallIntent class]]) {
|
||||
contacts = ((INStartVideoCallIntent *) intent).contacts;
|
||||
}
|
||||
|
||||
if (contacts && (url = contacts.firstObject.personHandle.value)) {
|
||||
return @{
|
||||
@"config": @{@"startAudioOnly":@(startAudioOnly)},
|
||||
@"url": url
|
||||
};
|
||||
return [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
|
||||
builder.audioOnly = audioOnly;
|
||||
builder.room = url;
|
||||
}];
|
||||
}
|
||||
} else if (self.conferenceActivityType && [activityType isEqualToString:self.conferenceActivityType]) {
|
||||
// App was started by continuing a registered NSUserActivity (SiriKit, Handoff, ...)
|
||||
NSString *url;
|
||||
|
||||
if ((url = userActivity.userInfo[@"url"])) {
|
||||
return @{ @"url" : url };
|
||||
return [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
|
||||
builder.room = url;
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -171,6 +182,10 @@
|
|||
|
||||
#pragma mark - Private API methods
|
||||
|
||||
- (NSDictionary *)getDefaultProps {
|
||||
return _defaultConferenceOptions == nil ? @{} : [_defaultConferenceOptions asProps];
|
||||
}
|
||||
|
||||
- (RCTBridge *)getReactBridge {
|
||||
return _bridgeWrapper.bridge;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "JitsiMeetConferenceOptions.h"
|
||||
|
||||
@interface JitsiMeetConferenceOptions ()
|
||||
|
||||
- (NSMutableDictionary *)asProps;
|
||||
|
||||
@end
|
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
@interface JitsiMeetConferenceOptionsBuilder : NSObject
|
||||
|
||||
@property (nonatomic, copy, nullable) NSURL *serverURL;
|
||||
@property (nonatomic, copy, nullable) NSString *room;
|
||||
@property (nonatomic, copy, nullable) NSString *token;
|
||||
|
||||
@property (nonatomic, copy, nullable) NSDictionary *colorScheme;
|
||||
|
||||
@property (nonatomic) BOOL audioOnly;
|
||||
@property (nonatomic) BOOL audioMuted;
|
||||
@property (nonatomic) BOOL videoMuted;
|
||||
|
||||
@property (nonatomic) BOOL welcomePageEnabled;
|
||||
|
||||
@end
|
||||
|
||||
@interface JitsiMeetConferenceOptions : NSObject
|
||||
|
||||
@property (nonatomic, copy, nullable, readonly) NSURL *serverURL;
|
||||
@property (nonatomic, copy, nullable, readonly) NSString *room;
|
||||
@property (nonatomic, copy, nullable, readonly) NSString *token;
|
||||
|
||||
@property (nonatomic, copy, nullable) NSDictionary *colorScheme;
|
||||
|
||||
@property (nonatomic, readonly) BOOL audioOnly;
|
||||
@property (nonatomic, readonly) BOOL audioMuted;
|
||||
@property (nonatomic, readonly) BOOL videoMuted;
|
||||
|
||||
@property (nonatomic, readonly) BOOL welcomePageEnabled;
|
||||
|
||||
+ (instancetype)fromBuilder:(void (^)(JitsiMeetConferenceOptionsBuilder *))initBlock;
|
||||
- (instancetype)init NS_UNAVAILABLE;
|
||||
|
||||
@end
|
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
* Copyright @ 2019-present 8x8, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <React/RCTUtils.h>
|
||||
|
||||
#import "JitsiMeetConferenceOptions+Private.h"
|
||||
|
||||
@implementation JitsiMeetConferenceOptionsBuilder {
|
||||
NSNumber *_audioOnly;
|
||||
NSNumber *_audioMuted;
|
||||
NSNumber *_videoMuted;
|
||||
NSNumber *_welcomePageEnabled;
|
||||
}
|
||||
|
||||
@dynamic audioOnly;
|
||||
@dynamic audioMuted;
|
||||
@dynamic videoMuted;
|
||||
@dynamic welcomePageEnabled;
|
||||
|
||||
- (instancetype)init {
|
||||
if (self = [super init]) {
|
||||
_serverURL = nil;
|
||||
_room = nil;
|
||||
_token = nil;
|
||||
|
||||
_colorScheme = nil;
|
||||
|
||||
_audioOnly = nil;
|
||||
_audioMuted = nil;
|
||||
_videoMuted = nil;
|
||||
|
||||
_welcomePageEnabled = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - Dynamic properties
|
||||
|
||||
- (void)setAudioOnly:(BOOL)audioOnly {
|
||||
_audioOnly = [NSNumber numberWithBool:audioOnly];
|
||||
}
|
||||
|
||||
- (BOOL)audioOnly {
|
||||
return _audioOnly && [_audioOnly boolValue];
|
||||
}
|
||||
|
||||
- (void)setAudioMuted:(BOOL)audioMuted {
|
||||
_audioMuted = [NSNumber numberWithBool:audioMuted];
|
||||
}
|
||||
|
||||
- (BOOL)audioMuted {
|
||||
return _audioMuted && [_audioMuted boolValue];
|
||||
}
|
||||
|
||||
- (void)setVideoMuted:(BOOL)videoMuted {
|
||||
_videoMuted = [NSNumber numberWithBool:videoMuted];
|
||||
}
|
||||
|
||||
- (BOOL)videoMuted {
|
||||
return _videoMuted && [_videoMuted boolValue];
|
||||
}
|
||||
|
||||
- (void)setWelcomePageEnabled:(BOOL)welcomePageEnabled {
|
||||
_welcomePageEnabled = [NSNumber numberWithBool:welcomePageEnabled];
|
||||
}
|
||||
|
||||
- (BOOL)welcomePageEnabled {
|
||||
return _welcomePageEnabled && [_welcomePageEnabled boolValue];
|
||||
}
|
||||
|
||||
#pragma mark - Private API
|
||||
|
||||
- (NSNumber *)getAudioOnly {
|
||||
return _audioOnly;
|
||||
}
|
||||
|
||||
- (NSNumber *)getAudioMuted {
|
||||
return _audioMuted;
|
||||
}
|
||||
|
||||
- (NSNumber *)getVideoMuted {
|
||||
return _videoMuted;
|
||||
}
|
||||
|
||||
- (NSNumber *)getWelcomePageEnabled {
|
||||
return _welcomePageEnabled;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation JitsiMeetConferenceOptions {
|
||||
NSNumber *_audioOnly;
|
||||
NSNumber *_audioMuted;
|
||||
NSNumber *_videoMuted;
|
||||
NSNumber *_welcomePageEnabled;
|
||||
}
|
||||
|
||||
@dynamic audioOnly;
|
||||
@dynamic audioMuted;
|
||||
@dynamic videoMuted;
|
||||
@dynamic welcomePageEnabled;
|
||||
|
||||
#pragma mark - Dynamic properties
|
||||
|
||||
- (BOOL)audioOnly {
|
||||
return _audioOnly && [_audioOnly boolValue];
|
||||
}
|
||||
|
||||
- (BOOL)audioMuted {
|
||||
return _audioMuted && [_audioMuted boolValue];
|
||||
}
|
||||
|
||||
- (BOOL)videoMuted {
|
||||
return _videoMuted && [_videoMuted boolValue];
|
||||
}
|
||||
|
||||
- (BOOL)welcomePageEnabled {
|
||||
return _welcomePageEnabled && [_welcomePageEnabled boolValue];
|
||||
}
|
||||
|
||||
#pragma mark - Internal initializer
|
||||
|
||||
- (instancetype)initWithBuilder:(JitsiMeetConferenceOptionsBuilder *)builder {
|
||||
if (self = [super init]) {
|
||||
_serverURL = builder.serverURL;
|
||||
_room = builder.room;
|
||||
_token = builder.token;
|
||||
|
||||
_colorScheme = builder.colorScheme;
|
||||
|
||||
_audioOnly = [builder getAudioOnly];
|
||||
_audioMuted = [builder getAudioMuted];
|
||||
_videoMuted = [builder getVideoMuted];
|
||||
|
||||
_welcomePageEnabled = [builder getWelcomePageEnabled];
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
#pragma mark - API
|
||||
|
||||
+ (instancetype)fromBuilder:(void (^)(JitsiMeetConferenceOptionsBuilder *))initBlock {
|
||||
JitsiMeetConferenceOptionsBuilder *builder = [[JitsiMeetConferenceOptionsBuilder alloc] init];
|
||||
initBlock(builder);
|
||||
return [[JitsiMeetConferenceOptions alloc] initWithBuilder:builder];
|
||||
}
|
||||
|
||||
#pragma mark - Private API
|
||||
|
||||
- (NSDictionary *)asProps {
|
||||
NSMutableDictionary *props = [[NSMutableDictionary alloc] init];
|
||||
|
||||
if (_colorScheme != nil) {
|
||||
props[@"colorScheme"] = self.colorScheme;
|
||||
}
|
||||
|
||||
if (_welcomePageEnabled != nil) {
|
||||
props[@"welcomePageEnabled"] = @(self.welcomePageEnabled);
|
||||
}
|
||||
|
||||
NSMutableDictionary *config = [[NSMutableDictionary alloc] init];
|
||||
if (_audioOnly != nil) {
|
||||
config[@"startAudioOnly"] = @(self.audioOnly);
|
||||
}
|
||||
if (_audioMuted != nil) {
|
||||
config[@"startWithAudioMuted"] = @(self.audioMuted);
|
||||
}
|
||||
if (_videoMuted != nil) {
|
||||
config[@"startWithVideoMuted"] = @(self.videoMuted);
|
||||
}
|
||||
|
||||
NSMutableDictionary *urlProps = [[NSMutableDictionary alloc] init];
|
||||
|
||||
// The room is fully qualified.
|
||||
if (_room != nil && [_room containsString:@"://"]) {
|
||||
urlProps[@"url"] = _room;
|
||||
} else {
|
||||
if (_serverURL != nil) {
|
||||
urlProps[@"serverURL"] = [_serverURL absoluteString];
|
||||
}
|
||||
|
||||
if (_room != nil) {
|
||||
urlProps[@"room"] = _room;
|
||||
}
|
||||
}
|
||||
|
||||
if (_token != nil) {
|
||||
urlProps[@"jwt"] = _token;
|
||||
}
|
||||
|
||||
urlProps[@"config"] = config;
|
||||
props[@"url"] = urlProps;
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
@end
|
|
@ -20,6 +20,6 @@
|
|||
@interface JitsiMeetView ()
|
||||
|
||||
+ (instancetype)viewForExternalAPIScope:(NSString *)externalAPIScope;
|
||||
+ (BOOL)loadURLInViews:(NSDictionary *)urlObject;
|
||||
+ (BOOL)setPropsInViews:(NSDictionary *_Nonnull)newProps;
|
||||
|
||||
@end
|
||||
|
|
|
@ -18,23 +18,14 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
#import "JitsiMeetConferenceOptions.h"
|
||||
#import "JitsiMeetViewDelegate.h"
|
||||
|
||||
@interface JitsiMeetView : UIView
|
||||
|
||||
@property (class, copy, nonatomic, nullable) NSString *conferenceActivityType;
|
||||
|
||||
@property (nonatomic) NSDictionary *colorScheme;
|
||||
|
||||
@property (copy, nonatomic, nullable) NSURL *defaultURL;
|
||||
|
||||
@property (nonatomic, nullable, weak) id<JitsiMeetViewDelegate> delegate;
|
||||
|
||||
@property (nonatomic) BOOL pictureInPictureEnabled;
|
||||
|
||||
@property (nonatomic) BOOL welcomePageEnabled;
|
||||
|
||||
- (void)join:(NSDictionary * _Nullable)url;
|
||||
- (void)join:(JitsiMeetConferenceOptions *)options;
|
||||
- (void)leave;
|
||||
|
||||
@end
|
||||
|
|
|
@ -20,7 +20,9 @@
|
|||
#import <React/RCTRootView.h>
|
||||
|
||||
#import "JitsiMeet+Private.h"
|
||||
#import "JitsiMeetConferenceOptions+Private.h"
|
||||
#import "JitsiMeetView+Private.h"
|
||||
#import "ReactUtils.h"
|
||||
|
||||
|
||||
@implementation JitsiMeetView {
|
||||
|
@ -32,12 +34,8 @@
|
|||
NSString *externalAPIScope;
|
||||
|
||||
RCTRootView *rootView;
|
||||
|
||||
NSNumber *_pictureInPictureEnabled;
|
||||
}
|
||||
|
||||
@dynamic pictureInPictureEnabled;
|
||||
|
||||
/**
|
||||
* The `JitsiMeetView`s associated with their `ExternalAPI` scopes (i.e. unique
|
||||
* identifiers within the process).
|
||||
|
@ -96,80 +94,46 @@ static void initializeViewsMap() {
|
|||
// parts of the application and causes less perceived visual flicker than
|
||||
// the default background color.
|
||||
self.backgroundColor
|
||||
= [UIColor colorWithRed:.07f green:.07f blue:.07f alpha:1];
|
||||
= [UIColor colorWithRed:.07f green:.07f blue:.07f alpha:1];
|
||||
}
|
||||
|
||||
#pragma mark API
|
||||
|
||||
- (void)join:(NSDictionary *_Nullable)url {
|
||||
[self loadURL:url];
|
||||
- (void)join:(JitsiMeetConferenceOptions *)options {
|
||||
[self setProps:options == nil ? @{} : [options asProps]];
|
||||
}
|
||||
|
||||
- (void)leave {
|
||||
[self loadURL:nil];
|
||||
}
|
||||
|
||||
#pragma pictureInPictureEnabled getter / setter
|
||||
|
||||
- (void) setPictureInPictureEnabled:(BOOL)pictureInPictureEnabled {
|
||||
_pictureInPictureEnabled
|
||||
= [NSNumber numberWithBool:pictureInPictureEnabled];
|
||||
}
|
||||
|
||||
- (BOOL) pictureInPictureEnabled {
|
||||
if (_pictureInPictureEnabled) {
|
||||
return [_pictureInPictureEnabled boolValue];
|
||||
}
|
||||
|
||||
// The SDK/JitsiMeetView client/consumer did not explicitly enable/disable
|
||||
// Picture-in-Picture. However, we may automatically deduce their
|
||||
// intentions: we need the support of the client in order to implement
|
||||
// Picture-in-Picture on iOS (in contrast to Android) so if the client
|
||||
// appears to have provided the support then we can assume that they did it
|
||||
// with the intention to have Picture-in-Picture enabled.
|
||||
return self.delegate
|
||||
&& [self.delegate respondsToSelector:@selector(enterPictureInPicture:)];
|
||||
[self setProps:@{}];
|
||||
}
|
||||
|
||||
#pragma mark Private methods
|
||||
|
||||
/**
|
||||
* Loads a specific URL which may identify a conference to join. The URL is
|
||||
* specified in the form of an `NSDictionary` of properties which (1)
|
||||
* internally are sufficient to construct a URL `NSString` while (2) abstracting
|
||||
* the specifics of constructing the URL away from API clients/consumers. If the
|
||||
* specified URL is `nil` and the Welcome page is enabled, the Welcome page is
|
||||
* displayed instead.
|
||||
* Passes the given props to the React Native application. The props which we pass
|
||||
* are a combination of 3 different sources:
|
||||
*
|
||||
* @param urlObject The URL to load which may identify a conference to join.
|
||||
* - JitsiMeet.defaultConferenceOptions
|
||||
* - This function's parameters
|
||||
* - Some extras which are added by this function
|
||||
*/
|
||||
- (void)loadURL:(NSDictionary *_Nullable)urlObject {
|
||||
NSMutableDictionary *props = [[NSMutableDictionary alloc] init];
|
||||
- (void)setProps:(NSDictionary *_Nonnull)newProps {
|
||||
NSMutableDictionary *props = mergeProps([[JitsiMeet sharedInstance] getDefaultProps], newProps);
|
||||
|
||||
if (self.defaultURL) {
|
||||
props[@"defaultURL"] = [self.defaultURL absoluteString];
|
||||
}
|
||||
|
||||
props[@"colorScheme"] = self.colorScheme;
|
||||
props[@"externalAPIScope"] = externalAPIScope;
|
||||
props[@"pictureInPictureEnabled"] = @(self.pictureInPictureEnabled);
|
||||
props[@"welcomePageEnabled"] = @(self.welcomePageEnabled);
|
||||
|
||||
// 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) {
|
||||
props[@"url"] = urlObject;
|
||||
}
|
||||
// TODO: put this in some 'flags' field
|
||||
props[@"pictureInPictureEnabled"]
|
||||
= @(self.delegate && [self.delegate respondsToSelector:@selector(enterPictureInPicture:)]);
|
||||
|
||||
// XXX The method loadURL: is supposed to be imperative i.e. a second
|
||||
// This method is supposed to be imperative i.e. a second
|
||||
// invocation with one and the same URL is expected to join the respective
|
||||
// conference again if the first invocation was followed by leaving the
|
||||
// conference. However, React and, respectively,
|
||||
// appProperties/initialProperties are declarative expressions i.e. one and
|
||||
// the same URL will not trigger an automatic re-render in the JavaScript
|
||||
// source code. The workaround implemented bellow introduces imperativeness
|
||||
// in React Component props by defining a unique value per loadURL:
|
||||
// invocation.
|
||||
// in React Component props by defining a unique value per invocation.
|
||||
props[@"timestamp"] = @(mach_absolute_time());
|
||||
|
||||
if (rootView) {
|
||||
|
@ -192,7 +156,7 @@ static void initializeViewsMap() {
|
|||
}
|
||||
}
|
||||
|
||||
+ (BOOL)loadURLInViews:(NSDictionary *)urlObject {
|
||||
+ (BOOL)setPropsInViews:(NSDictionary *_Nonnull)newProps {
|
||||
BOOL handled = NO;
|
||||
|
||||
if (views) {
|
||||
|
@ -201,7 +165,7 @@ static void initializeViewsMap() {
|
|||
= [self viewForExternalAPIScope:externalAPIScope];
|
||||
|
||||
if (view) {
|
||||
[view loadURL:urlObject];
|
||||
[view setProps:newProps];
|
||||
handled = YES;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,9 +14,10 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ReactUtils_h
|
||||
#define ReactUtils_h
|
||||
#ifndef JM_REACTUTILS_H
|
||||
#define JM_REACTUTILS_H
|
||||
|
||||
NSMutableDictionary* mergeProps(NSDictionary *a, NSDictionary *b);
|
||||
void registerReactFatalErrorHandler(void);
|
||||
|
||||
#endif /* ReactUtils_h */
|
||||
#endif /* JM_REACTUTILS_H */
|
||||
|
|
|
@ -18,6 +18,46 @@
|
|||
|
||||
#import "ReactUtils.h"
|
||||
|
||||
#pragma mark - Utility functions
|
||||
|
||||
/**
|
||||
* Merges 2 sets of props into a single one.
|
||||
*/
|
||||
NSMutableDictionary* mergeProps(NSDictionary *a, NSDictionary *b) {
|
||||
if (a == nil) {
|
||||
return [NSMutableDictionary dictionaryWithDictionary:b == nil ? @{} : b];
|
||||
}
|
||||
|
||||
if (b == nil) {
|
||||
return [NSMutableDictionary dictionaryWithDictionary:a];
|
||||
}
|
||||
|
||||
// Both have values, let's merge them, the strategy is to take the value from a first,
|
||||
// then override it with the one from b. If the value is a dictionary, merge them
|
||||
// recursively. Same goes for arrays.
|
||||
NSMutableDictionary *result = [NSMutableDictionary dictionaryWithDictionary:a];
|
||||
|
||||
for (NSString *key in b) {
|
||||
id value = b[key];
|
||||
id aValue = result[key];
|
||||
|
||||
if (aValue == nil) {
|
||||
result[key] = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ([value isKindOfClass:NSArray.class]) {
|
||||
result[key] = [aValue arrayByAddingObjectsFromArray:value];
|
||||
} else if ([value isKindOfClass:NSDictionary.class]) {
|
||||
result[key] = mergeProps(aValue, value);
|
||||
} else {
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* A `RCTFatalHandler` implementation which swallows JavaScript errors. In the
|
||||
* Release configuration, React Native will (intentionally) raise an unhandled
|
||||
|
|
Loading…
Reference in New Issue