Browse Source

ios: introduce JitsiMeetConferenceOptions

j8
Saúl Ibarra Corretgé 6 years ago
parent
commit
aedcfba263

+ 11
- 4
ios/app/src/AppDelegate.m View File

37
         [Fabric with:@[[Crashlytics class]]];
37
         [Fabric with:@[[Crashlytics class]]];
38
     }
38
     }
39
 
39
 
40
-    [JitsiMeet sharedInstance].conferenceActivityType = JitsiMeetConferenceActivityType;
41
-    [JitsiMeet sharedInstance].customUrlScheme = @"org.jitsi.meet";
42
-    [JitsiMeet sharedInstance].universalLinkDomains = @[@"meet.jit.si", @"beta.meet.jit.si"];
40
+    JitsiMeet *jitsiMeet = [JitsiMeet sharedInstance];
43
 
41
 
44
-    [[JitsiMeet sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions];
42
+    jitsiMeet.conferenceActivityType = JitsiMeetConferenceActivityType;
43
+    jitsiMeet.customUrlScheme = @"org.jitsi.meet";
44
+    jitsiMeet.universalLinkDomains = @[@"meet.jit.si", @"beta.meet.jit.si"];
45
+
46
+    jitsiMeet.defaultConferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
47
+        builder.serverURL = [NSURL URLWithString:@"https://meet.jit.si"];
48
+        builder.welcomePageEnabled = YES;
49
+    }];
50
+
51
+    [jitsiMeet application:application didFinishLaunchingWithOptions:launchOptions];
45
 
52
 
46
     return YES;
53
     return YES;
47
 }
54
 }

+ 3
- 7
ios/app/src/ViewController.m View File

21
 @import MobileCoreServices;
21
 @import MobileCoreServices;
22
 @import Intents;  // Needed for NSUserActivity suggestedInvocationPhrase
22
 @import Intents;  // Needed for NSUserActivity suggestedInvocationPhrase
23
 
23
 
24
+@import JitsiMeet;
25
+
24
 #import "Types.h"
26
 #import "Types.h"
25
 #import "ViewController.h"
27
 #import "ViewController.h"
26
 
28
 
33
     JitsiMeetView *view = (JitsiMeetView *) self.view;
35
     JitsiMeetView *view = (JitsiMeetView *) self.view;
34
     view.delegate = self;
36
     view.delegate = self;
35
 
37
 
36
-    // As this is the Jitsi Meet app (i.e. not the Jitsi Meet SDK), we do want
37
-    // the Welcome page to be enabled. It defaults to disabled in the SDK at the
38
-    // time of this writing but it is clearer to be explicit about what we want
39
-    // anyway.
40
-    view.welcomePageEnabled = YES;
41
-
42
-    [view join:[[JitsiMeet sharedInstance] getInitialURL]];
38
+    [view join:[[JitsiMeet sharedInstance] getInitialConferenceOptions]];
43
 }
39
 }
44
 
40
 
45
 // JitsiMeetViewDelegate
41
 // JitsiMeetViewDelegate

+ 10
- 2
ios/sdk/sdk.xcodeproj/project.pbxproj View File

42
 		C69EFA0E209A0F660027712B /* JMCallKitListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69EFA0B209A0F660027712B /* JMCallKitListener.swift */; };
42
 		C69EFA0E209A0F660027712B /* JMCallKitListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69EFA0B209A0F660027712B /* JMCallKitListener.swift */; };
43
 		C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425E204EF76800E062DD /* DragGestureController.swift */; };
43
 		C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425E204EF76800E062DD /* DragGestureController.swift */; };
44
 		C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */; };
44
 		C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */; };
45
-		C6F99C15204DB63E0001F710 /* JitsiMeetView+Private.h in Headers */ = {isa = PBXBuildFile; fileRef = C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */; };
45
+		DEAD3226220C497000E93636 /* JitsiMeetConferenceOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = DEAD3224220C497000E93636 /* JitsiMeetConferenceOptions.h */; settings = {ATTRIBUTES = (Public, ); }; };
46
+		DEAD3227220C497000E93636 /* JitsiMeetConferenceOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = DEAD3225220C497000E93636 /* JitsiMeetConferenceOptions.m */; };
46
 		DEFC743F21B178FA00E4DD96 /* LocaleDetector.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */; };
47
 		DEFC743F21B178FA00E4DD96 /* LocaleDetector.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */; };
47
 		DEFE535421FB1BF800011A3A /* JitsiMeet.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFE535321FB1BF800011A3A /* JitsiMeet.m */; };
48
 		DEFE535421FB1BF800011A3A /* JitsiMeet.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFE535321FB1BF800011A3A /* JitsiMeet.m */; };
48
 		DEFE535621FB2E8300011A3A /* ReactUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFE535521FB2E8300011A3A /* ReactUtils.m */; };
49
 		DEFE535621FB2E8300011A3A /* ReactUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = DEFE535521FB2E8300011A3A /* ReactUtils.m */; };
92
 		C6A3425E204EF76800E062DD /* DragGestureController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DragGestureController.swift; sourceTree = "<group>"; };
93
 		C6A3425E204EF76800E062DD /* DragGestureController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DragGestureController.swift; sourceTree = "<group>"; };
93
 		C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiPViewCoordinator.swift; sourceTree = "<group>"; };
94
 		C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiPViewCoordinator.swift; sourceTree = "<group>"; };
94
 		C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
95
 		C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = "<group>"; };
96
+		DEAD3224220C497000E93636 /* JitsiMeetConferenceOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetConferenceOptions.h; sourceTree = "<group>"; };
97
+		DEAD3225220C497000E93636 /* JitsiMeetConferenceOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeetConferenceOptions.m; sourceTree = "<group>"; };
98
+		DEAD3228220C734300E93636 /* JitsiMeetConferenceOptions+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetConferenceOptions+Private.h"; sourceTree = "<group>"; };
95
 		DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LocaleDetector.m; sourceTree = "<group>"; };
99
 		DEFC743D21B178FA00E4DD96 /* LocaleDetector.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LocaleDetector.m; sourceTree = "<group>"; };
96
 		DEFE535321FB1BF800011A3A /* JitsiMeet.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeet.m; sourceTree = "<group>"; };
100
 		DEFE535321FB1BF800011A3A /* JitsiMeet.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeet.m; sourceTree = "<group>"; };
97
 		DEFE535521FB2E8300011A3A /* ReactUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactUtils.m; sourceTree = "<group>"; };
101
 		DEFE535521FB2E8300011A3A /* ReactUtils.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ReactUtils.m; sourceTree = "<group>"; };
164
 				0BD906E81EC0C00300C8C18E /* JitsiMeet.h */,
168
 				0BD906E81EC0C00300C8C18E /* JitsiMeet.h */,
165
 				DEFE535821FB311F00011A3A /* JitsiMeet+Private.h */,
169
 				DEFE535821FB311F00011A3A /* JitsiMeet+Private.h */,
166
 				DEFE535321FB1BF800011A3A /* JitsiMeet.m */,
170
 				DEFE535321FB1BF800011A3A /* JitsiMeet.m */,
171
+				DEAD3224220C497000E93636 /* JitsiMeetConferenceOptions.h */,
172
+				DEAD3228220C734300E93636 /* JitsiMeetConferenceOptions+Private.h */,
173
+				DEAD3225220C497000E93636 /* JitsiMeetConferenceOptions.m */,
167
 				0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
174
 				0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */,
168
 				0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
175
 				0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */,
169
 				C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */,
176
 				C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */,
247
 			isa = PBXHeadersBuildPhase;
254
 			isa = PBXHeadersBuildPhase;
248
 			buildActionMask = 2147483647;
255
 			buildActionMask = 2147483647;
249
 			files = (
256
 			files = (
250
-				C6F99C15204DB63E0001F710 /* JitsiMeetView+Private.h in Headers */,
251
 				0B412F181EDEC65D00B1A0A6 /* JitsiMeetView.h in Headers */,
257
 				0B412F181EDEC65D00B1A0A6 /* JitsiMeetView.h in Headers */,
252
 				0B93EF7E1EC9DDCD0030D24D /* RCTBridgeWrapper.h in Headers */,
258
 				0B93EF7E1EC9DDCD0030D24D /* RCTBridgeWrapper.h in Headers */,
253
 				0B412F221EDEF6EA00B1A0A6 /* JitsiMeetViewDelegate.h in Headers */,
259
 				0B412F221EDEF6EA00B1A0A6 /* JitsiMeetViewDelegate.h in Headers */,
254
 				0BD906EA1EC0C00300C8C18E /* JitsiMeet.h in Headers */,
260
 				0BD906EA1EC0C00300C8C18E /* JitsiMeet.h in Headers */,
261
+				DEAD3226220C497000E93636 /* JitsiMeetConferenceOptions.h in Headers */,
255
 			);
262
 			);
256
 			runOnlyForDeploymentPostprocessing = 0;
263
 			runOnlyForDeploymentPostprocessing = 0;
257
 		};
264
 		};
451
 			files = (
458
 			files = (
452
 				0BB9AD7B1F5EC8F4001C08DB /* CallKit.m in Sources */,
459
 				0BB9AD7B1F5EC8F4001C08DB /* CallKit.m in Sources */,
453
 				0BB9AD7D1F60356D001C08DB /* AppInfo.m in Sources */,
460
 				0BB9AD7D1F60356D001C08DB /* AppInfo.m in Sources */,
461
+				DEAD3227220C497000E93636 /* JitsiMeetConferenceOptions.m in Sources */,
454
 				0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
462
 				0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */,
455
 				0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
463
 				0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */,
456
 				0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */,
464
 				0BCA49601EC4B6C600B793EE /* POSIX.m in Sources */,

+ 1
- 0
ios/sdk/src/JitsiMeet+Private.h View File

18
 
18
 
19
 @interface JitsiMeet ()
19
 @interface JitsiMeet ()
20
 
20
 
21
+- (NSDictionary *)getDefaultProps;
21
 - (RCTBridge *)getReactBridge;
22
 - (RCTBridge *)getReactBridge;
22
 
23
 
23
 @end
24
 @end

+ 4
- 1
ios/sdk/src/JitsiMeet.h View File

17
 
17
 
18
 #import <JitsiMeet/JitsiMeetView.h>
18
 #import <JitsiMeet/JitsiMeetView.h>
19
 #import <JitsiMeet/JitsiMeetViewDelegate.h>
19
 #import <JitsiMeet/JitsiMeetViewDelegate.h>
20
+#import <JitsiMeet/JitsiMeetConferenceOptions.h>
20
 
21
 
21
 
22
 
22
 @interface JitsiMeet : NSObject
23
 @interface JitsiMeet : NSObject
25
 @property (copy, nonatomic, nullable) NSString *customUrlScheme;
26
 @property (copy, nonatomic, nullable) NSString *customUrlScheme;
26
 @property (copy, nonatomic, nullable) NSArray<NSString *> *universalLinkDomains;
27
 @property (copy, nonatomic, nullable) NSArray<NSString *> *universalLinkDomains;
27
 
28
 
29
+@property (nonatomic, nullable) JitsiMeetConferenceOptions *defaultConferenceOptions;
30
+
28
 #pragma mak - This class is a singleton
31
 #pragma mak - This class is a singleton
29
 
32
 
30
 + (instancetype)sharedInstance;
33
 + (instancetype)sharedInstance;
44
 
47
 
45
 #pragma mark - Utility methods
48
 #pragma mark - Utility methods
46
 
49
 
47
-- (NSDictionary *)getInitialURL;
50
+- (JitsiMeetConferenceOptions *)getInitialConferenceOptions;
48
 
51
 
49
 @end
52
 @end

+ 30
- 15
ios/sdk/src/JitsiMeet.m View File

18
 
18
 
19
 #import "Dropbox.h"
19
 #import "Dropbox.h"
20
 #import "JitsiMeet+Private.h"
20
 #import "JitsiMeet+Private.h"
21
+#import "JitsiMeetConferenceOptions+Private.h"
21
 #import "JitsiMeetView+Private.h"
22
 #import "JitsiMeetView+Private.h"
22
 #import "RCTBridgeWrapper.h"
23
 #import "RCTBridgeWrapper.h"
23
 #import "ReactUtils.h"
24
 #import "ReactUtils.h"
71
   continueUserActivity:(NSUserActivity *)userActivity
72
   continueUserActivity:(NSUserActivity *)userActivity
72
     restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {
73
     restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler {
73
 
74
 
74
-    id url = [self urlFromUserActivity:userActivity];
75
+    JitsiMeetConferenceOptions *options = [self optionsFromUserActivity:userActivity];
75
 
76
 
76
-    return url && [JitsiMeetView loadURLInViews:url];
77
+    return options && [JitsiMeetView setPropsInViews:[options asProps]];
77
 }
78
 }
78
 
79
 
79
 - (BOOL)application:(UIApplication *)app
80
 - (BOOL)application:(UIApplication *)app
95
         return NO;
96
         return NO;
96
     }
97
     }
97
 
98
 
98
-    return [JitsiMeetView loadURLInViews:@{ @"url" : url.absoluteString }];
99
+    JitsiMeetConferenceOptions *conferenceOptions = [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
100
+        builder.room = [url absoluteString];
101
+    }];
102
+    
103
+    return [JitsiMeetView setPropsInViews:[conferenceOptions asProps]];
99
 }
104
 }
100
 
105
 
101
 #pragma mark - Utility methods
106
 #pragma mark - Utility methods
102
 
107
 
103
-- (NSDictionary *)getInitialURL {
108
+- (JitsiMeetConferenceOptions *)getInitialConferenceOptions {
104
     if (_launchOptions[UIApplicationLaunchOptionsURLKey]) {
109
     if (_launchOptions[UIApplicationLaunchOptionsURLKey]) {
105
         NSURL *url = _launchOptions[UIApplicationLaunchOptionsURLKey];
110
         NSURL *url = _launchOptions[UIApplicationLaunchOptionsURLKey];
106
-        return @{ @"url" : url.absoluteString };
111
+        return [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
112
+            builder.room = [url absoluteString];
113
+        }];
107
     } else {
114
     } else {
108
         NSDictionary *userActivityDictionary
115
         NSDictionary *userActivityDictionary
109
             = _launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey];
116
             = _launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey];
110
         NSUserActivity *userActivity
117
         NSUserActivity *userActivity
111
             = [userActivityDictionary objectForKey:@"UIApplicationLaunchOptionsUserActivityKey"];
118
             = [userActivityDictionary objectForKey:@"UIApplicationLaunchOptionsUserActivityKey"];
112
         if (userActivity != nil) {
119
         if (userActivity != nil) {
113
-            return [self urlFromUserActivity:userActivity];
120
+            return [self optionsFromUserActivity:userActivity];
114
         }
121
         }
115
     }
122
     }
116
 
123
 
117
     return nil;
124
     return nil;
118
 }
125
 }
119
 
126
 
120
-- (NSDictionary *)urlFromUserActivity:(NSUserActivity *)userActivity {
127
+- (JitsiMeetConferenceOptions *)optionsFromUserActivity:(NSUserActivity *)userActivity {
121
     NSString *activityType = userActivity.activityType;
128
     NSString *activityType = userActivity.activityType;
122
 
129
 
123
     if ([activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
130
     if ([activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
124
         // App was started by opening a URL in the browser
131
         // App was started by opening a URL in the browser
125
         NSURL *url = userActivity.webpageURL;
132
         NSURL *url = userActivity.webpageURL;
126
         if ([_universalLinkDomains containsObject:url.host]) {
133
         if ([_universalLinkDomains containsObject:url.host]) {
127
-            return @{ @"url" : url.absoluteString };
134
+            return [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
135
+                builder.room = [url absoluteString];
136
+            }];
128
         }
137
         }
129
     } else if ([activityType isEqualToString:@"INStartAudioCallIntent"]
138
     } else if ([activityType isEqualToString:@"INStartAudioCallIntent"]
130
                || [activityType isEqualToString:@"INStartVideoCallIntent"]) {
139
                || [activityType isEqualToString:@"INStartVideoCallIntent"]) {
132
         INIntent *intent = userActivity.interaction.intent;
141
         INIntent *intent = userActivity.interaction.intent;
133
         NSArray<INPerson *> *contacts;
142
         NSArray<INPerson *> *contacts;
134
         NSString *url;
143
         NSString *url;
135
-        BOOL startAudioOnly = NO;
144
+        BOOL audioOnly = NO;
136
 
145
 
137
         if ([intent isKindOfClass:[INStartAudioCallIntent class]]) {
146
         if ([intent isKindOfClass:[INStartAudioCallIntent class]]) {
138
             contacts = ((INStartAudioCallIntent *) intent).contacts;
147
             contacts = ((INStartAudioCallIntent *) intent).contacts;
139
-            startAudioOnly = YES;
148
+            audioOnly = YES;
140
         } else if ([intent isKindOfClass:[INStartVideoCallIntent class]]) {
149
         } else if ([intent isKindOfClass:[INStartVideoCallIntent class]]) {
141
             contacts = ((INStartVideoCallIntent *) intent).contacts;
150
             contacts = ((INStartVideoCallIntent *) intent).contacts;
142
         }
151
         }
143
 
152
 
144
         if (contacts && (url = contacts.firstObject.personHandle.value)) {
153
         if (contacts && (url = contacts.firstObject.personHandle.value)) {
145
-            return @{
146
-                     @"config": @{@"startAudioOnly":@(startAudioOnly)},
147
-                     @"url": url
148
-                     };
154
+            return [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
155
+                builder.audioOnly = audioOnly;
156
+                builder.room = url;
157
+            }];
149
         }
158
         }
150
     } else if (self.conferenceActivityType && [activityType isEqualToString:self.conferenceActivityType]) {
159
     } else if (self.conferenceActivityType && [activityType isEqualToString:self.conferenceActivityType]) {
151
         // App was started by continuing a registered NSUserActivity (SiriKit, Handoff, ...)
160
         // App was started by continuing a registered NSUserActivity (SiriKit, Handoff, ...)
152
         NSString *url;
161
         NSString *url;
153
 
162
 
154
         if ((url = userActivity.userInfo[@"url"])) {
163
         if ((url = userActivity.userInfo[@"url"])) {
155
-            return @{ @"url" : url };
164
+            return [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
165
+                builder.room = url;
166
+            }];
156
         }
167
         }
157
     }
168
     }
158
 
169
 
171
 
182
 
172
 #pragma mark - Private API methods
183
 #pragma mark - Private API methods
173
 
184
 
185
+- (NSDictionary *)getDefaultProps {
186
+    return _defaultConferenceOptions == nil ? @{} : [_defaultConferenceOptions asProps];
187
+}
188
+
174
 - (RCTBridge *)getReactBridge {
189
 - (RCTBridge *)getReactBridge {
175
     return _bridgeWrapper.bridge;
190
     return _bridgeWrapper.bridge;
176
 }
191
 }

+ 23
- 0
ios/sdk/src/JitsiMeetConferenceOptions+Private.h View File

1
+/*
2
+ * Copyright @ 2019-present 8x8, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ *     http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+#import "JitsiMeetConferenceOptions.h"
18
+
19
+@interface JitsiMeetConferenceOptions ()
20
+
21
+- (NSMutableDictionary *)asProps;
22
+
23
+@end

+ 52
- 0
ios/sdk/src/JitsiMeetConferenceOptions.h View File

1
+/*
2
+ * Copyright @ 2019-present 8x8, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ *     http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+#import <Foundation/Foundation.h>
18
+
19
+@interface JitsiMeetConferenceOptionsBuilder : NSObject
20
+
21
+@property (nonatomic, copy, nullable) NSURL *serverURL;
22
+@property (nonatomic, copy, nullable) NSString *room;
23
+@property (nonatomic, copy, nullable) NSString *token;
24
+
25
+@property (nonatomic, copy, nullable) NSDictionary *colorScheme;
26
+
27
+@property (nonatomic) BOOL audioOnly;
28
+@property (nonatomic) BOOL audioMuted;
29
+@property (nonatomic) BOOL videoMuted;
30
+
31
+@property (nonatomic) BOOL welcomePageEnabled;
32
+
33
+@end
34
+
35
+@interface JitsiMeetConferenceOptions : NSObject
36
+
37
+@property (nonatomic, copy, nullable, readonly) NSURL *serverURL;
38
+@property (nonatomic, copy, nullable, readonly) NSString *room;
39
+@property (nonatomic, copy, nullable, readonly) NSString *token;
40
+
41
+@property (nonatomic, copy, nullable) NSDictionary *colorScheme;
42
+
43
+@property (nonatomic, readonly) BOOL audioOnly;
44
+@property (nonatomic, readonly) BOOL audioMuted;
45
+@property (nonatomic, readonly) BOOL videoMuted;
46
+
47
+@property (nonatomic, readonly) BOOL welcomePageEnabled;
48
+
49
++ (instancetype)fromBuilder:(void (^)(JitsiMeetConferenceOptionsBuilder *))initBlock;
50
+- (instancetype)init NS_UNAVAILABLE;
51
+
52
+@end

+ 212
- 0
ios/sdk/src/JitsiMeetConferenceOptions.m View File

1
+/*
2
+ * Copyright @ 2019-present 8x8, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ *     http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+#import <React/RCTUtils.h>
18
+
19
+#import "JitsiMeetConferenceOptions+Private.h"
20
+
21
+@implementation JitsiMeetConferenceOptionsBuilder {
22
+    NSNumber *_audioOnly;
23
+    NSNumber *_audioMuted;
24
+    NSNumber *_videoMuted;
25
+    NSNumber *_welcomePageEnabled;
26
+}
27
+
28
+@dynamic audioOnly;
29
+@dynamic audioMuted;
30
+@dynamic videoMuted;
31
+@dynamic welcomePageEnabled;
32
+
33
+- (instancetype)init {
34
+    if (self = [super init]) {
35
+        _serverURL = nil;
36
+        _room = nil;
37
+        _token = nil;
38
+
39
+        _colorScheme = nil;
40
+
41
+        _audioOnly = nil;
42
+        _audioMuted = nil;
43
+        _videoMuted = nil;
44
+
45
+        _welcomePageEnabled = nil;
46
+    }
47
+    
48
+    return self;
49
+}
50
+
51
+#pragma mark - Dynamic properties
52
+
53
+- (void)setAudioOnly:(BOOL)audioOnly {
54
+    _audioOnly = [NSNumber numberWithBool:audioOnly];
55
+}
56
+
57
+- (BOOL)audioOnly {
58
+    return _audioOnly && [_audioOnly boolValue];
59
+}
60
+
61
+- (void)setAudioMuted:(BOOL)audioMuted {
62
+    _audioMuted = [NSNumber numberWithBool:audioMuted];
63
+}
64
+
65
+- (BOOL)audioMuted {
66
+    return _audioMuted && [_audioMuted boolValue];
67
+}
68
+
69
+- (void)setVideoMuted:(BOOL)videoMuted {
70
+    _videoMuted = [NSNumber numberWithBool:videoMuted];
71
+}
72
+
73
+- (BOOL)videoMuted {
74
+    return _videoMuted && [_videoMuted boolValue];
75
+}
76
+
77
+- (void)setWelcomePageEnabled:(BOOL)welcomePageEnabled {
78
+    _welcomePageEnabled = [NSNumber numberWithBool:welcomePageEnabled];
79
+}
80
+
81
+- (BOOL)welcomePageEnabled {
82
+    return _welcomePageEnabled && [_welcomePageEnabled boolValue];
83
+}
84
+
85
+#pragma mark - Private API
86
+
87
+- (NSNumber *)getAudioOnly {
88
+    return _audioOnly;
89
+}
90
+
91
+- (NSNumber *)getAudioMuted {
92
+    return _audioMuted;
93
+}
94
+
95
+- (NSNumber *)getVideoMuted {
96
+    return _videoMuted;
97
+}
98
+
99
+- (NSNumber *)getWelcomePageEnabled {
100
+    return _welcomePageEnabled;
101
+}
102
+
103
+@end
104
+
105
+@implementation JitsiMeetConferenceOptions {
106
+    NSNumber *_audioOnly;
107
+    NSNumber *_audioMuted;
108
+    NSNumber *_videoMuted;
109
+    NSNumber *_welcomePageEnabled;
110
+}
111
+
112
+@dynamic audioOnly;
113
+@dynamic audioMuted;
114
+@dynamic videoMuted;
115
+@dynamic welcomePageEnabled;
116
+
117
+#pragma mark - Dynamic properties
118
+
119
+- (BOOL)audioOnly {
120
+    return _audioOnly && [_audioOnly boolValue];
121
+}
122
+
123
+- (BOOL)audioMuted {
124
+    return _audioMuted && [_audioMuted boolValue];
125
+}
126
+
127
+- (BOOL)videoMuted {
128
+    return _videoMuted && [_videoMuted boolValue];
129
+}
130
+
131
+- (BOOL)welcomePageEnabled {
132
+    return _welcomePageEnabled && [_welcomePageEnabled boolValue];
133
+}
134
+
135
+#pragma mark - Internal initializer
136
+
137
+- (instancetype)initWithBuilder:(JitsiMeetConferenceOptionsBuilder *)builder {
138
+    if (self = [super init]) {
139
+        _serverURL = builder.serverURL;
140
+        _room = builder.room;
141
+        _token = builder.token;
142
+
143
+        _colorScheme = builder.colorScheme;
144
+
145
+        _audioOnly = [builder getAudioOnly];
146
+        _audioMuted = [builder getAudioMuted];
147
+        _videoMuted = [builder getVideoMuted];
148
+
149
+        _welcomePageEnabled = [builder getWelcomePageEnabled];
150
+    }
151
+
152
+    return self;
153
+}
154
+
155
+#pragma mark - API
156
+
157
++ (instancetype)fromBuilder:(void (^)(JitsiMeetConferenceOptionsBuilder *))initBlock {
158
+    JitsiMeetConferenceOptionsBuilder *builder = [[JitsiMeetConferenceOptionsBuilder alloc] init];
159
+    initBlock(builder);
160
+    return [[JitsiMeetConferenceOptions alloc] initWithBuilder:builder];
161
+}
162
+
163
+#pragma mark - Private API
164
+
165
+- (NSDictionary *)asProps {
166
+    NSMutableDictionary *props = [[NSMutableDictionary alloc] init];
167
+
168
+    if (_colorScheme != nil) {
169
+        props[@"colorScheme"] = self.colorScheme;
170
+    }
171
+
172
+    if (_welcomePageEnabled != nil) {
173
+        props[@"welcomePageEnabled"] = @(self.welcomePageEnabled);
174
+    }
175
+
176
+    NSMutableDictionary *config = [[NSMutableDictionary alloc] init];
177
+    if (_audioOnly != nil) {
178
+        config[@"startAudioOnly"] = @(self.audioOnly);
179
+    }
180
+    if (_audioMuted != nil) {
181
+        config[@"startWithAudioMuted"] = @(self.audioMuted);
182
+    }
183
+    if (_videoMuted != nil) {
184
+        config[@"startWithVideoMuted"] = @(self.videoMuted);
185
+    }
186
+
187
+    NSMutableDictionary *urlProps = [[NSMutableDictionary alloc] init];
188
+
189
+    // The room is fully qualified.
190
+    if (_room != nil && [_room containsString:@"://"]) {
191
+        urlProps[@"url"] = _room;
192
+    } else {
193
+        if (_serverURL != nil) {
194
+            urlProps[@"serverURL"] = [_serverURL absoluteString];
195
+        }
196
+
197
+        if (_room != nil) {
198
+            urlProps[@"room"] = _room;
199
+        }
200
+    }
201
+
202
+    if (_token != nil) {
203
+        urlProps[@"jwt"] = _token;
204
+    }
205
+
206
+    urlProps[@"config"] = config;
207
+    props[@"url"] = urlProps;
208
+
209
+    return props;
210
+}
211
+
212
+@end

+ 1
- 1
ios/sdk/src/JitsiMeetView+Private.h View File

20
 @interface JitsiMeetView ()
20
 @interface JitsiMeetView ()
21
 
21
 
22
 + (instancetype)viewForExternalAPIScope:(NSString *)externalAPIScope;
22
 + (instancetype)viewForExternalAPIScope:(NSString *)externalAPIScope;
23
-+ (BOOL)loadURLInViews:(NSDictionary *)urlObject;
23
++ (BOOL)setPropsInViews:(NSDictionary *_Nonnull)newProps;
24
 
24
 
25
 @end
25
 @end

+ 2
- 11
ios/sdk/src/JitsiMeetView.h View File

18
 #import <Foundation/Foundation.h>
18
 #import <Foundation/Foundation.h>
19
 #import <UIKit/UIKit.h>
19
 #import <UIKit/UIKit.h>
20
 
20
 
21
+#import "JitsiMeetConferenceOptions.h"
21
 #import "JitsiMeetViewDelegate.h"
22
 #import "JitsiMeetViewDelegate.h"
22
 
23
 
23
 @interface JitsiMeetView : UIView
24
 @interface JitsiMeetView : UIView
24
 
25
 
25
-@property (class, copy, nonatomic, nullable) NSString *conferenceActivityType;
26
-
27
-@property (nonatomic) NSDictionary *colorScheme;
28
-
29
-@property (copy, nonatomic, nullable) NSURL *defaultURL;
30
-
31
 @property (nonatomic, nullable, weak) id<JitsiMeetViewDelegate> delegate;
26
 @property (nonatomic, nullable, weak) id<JitsiMeetViewDelegate> delegate;
32
 
27
 
33
-@property (nonatomic) BOOL pictureInPictureEnabled;
34
-
35
-@property (nonatomic) BOOL welcomePageEnabled;
36
-
37
-- (void)join:(NSDictionary * _Nullable)url;
28
+- (void)join:(JitsiMeetConferenceOptions *)options;
38
 - (void)leave;
29
 - (void)leave;
39
 
30
 
40
 @end
31
 @end

+ 20
- 56
ios/sdk/src/JitsiMeetView.m View File

20
 #import <React/RCTRootView.h>
20
 #import <React/RCTRootView.h>
21
 
21
 
22
 #import "JitsiMeet+Private.h"
22
 #import "JitsiMeet+Private.h"
23
+#import "JitsiMeetConferenceOptions+Private.h"
23
 #import "JitsiMeetView+Private.h"
24
 #import "JitsiMeetView+Private.h"
25
+#import "ReactUtils.h"
24
 
26
 
25
 
27
 
26
 @implementation JitsiMeetView {
28
 @implementation JitsiMeetView {
32
     NSString *externalAPIScope;
34
     NSString *externalAPIScope;
33
 
35
 
34
     RCTRootView *rootView;
36
     RCTRootView *rootView;
35
-
36
-    NSNumber *_pictureInPictureEnabled;
37
 }
37
 }
38
 
38
 
39
-@dynamic pictureInPictureEnabled;
40
-
41
 /**
39
 /**
42
  * The `JitsiMeetView`s associated with their `ExternalAPI` scopes (i.e. unique
40
  * The `JitsiMeetView`s associated with their `ExternalAPI` scopes (i.e. unique
43
  * identifiers within the process).
41
  * identifiers within the process).
96
     // parts of the application and causes less perceived visual flicker than
94
     // parts of the application and causes less perceived visual flicker than
97
     // the default background color.
95
     // the default background color.
98
     self.backgroundColor
96
     self.backgroundColor
99
-    = [UIColor colorWithRed:.07f green:.07f blue:.07f alpha:1];
97
+        = [UIColor colorWithRed:.07f green:.07f blue:.07f alpha:1];
100
 }
98
 }
101
 
99
 
102
 #pragma mark API
100
 #pragma mark API
103
 
101
 
104
-- (void)join:(NSDictionary *_Nullable)url {
105
-    [self loadURL:url];
102
+- (void)join:(JitsiMeetConferenceOptions *)options {
103
+    [self setProps:options == nil ? @{} : [options asProps]];
106
 }
104
 }
107
 
105
 
108
 - (void)leave {
106
 - (void)leave {
109
-    [self loadURL:nil];
110
-}
111
-
112
-#pragma pictureInPictureEnabled getter / setter
113
-
114
-- (void) setPictureInPictureEnabled:(BOOL)pictureInPictureEnabled {
115
-    _pictureInPictureEnabled
116
-        = [NSNumber numberWithBool:pictureInPictureEnabled];
117
-}
118
-
119
-- (BOOL) pictureInPictureEnabled {
120
-    if (_pictureInPictureEnabled) {
121
-        return [_pictureInPictureEnabled boolValue];
122
-    }
123
-
124
-    // The SDK/JitsiMeetView client/consumer did not explicitly enable/disable
125
-    // Picture-in-Picture. However, we may automatically deduce their
126
-    // intentions: we need the support of the client in order to implement
127
-    // Picture-in-Picture on iOS (in contrast to Android) so if the client
128
-    // appears to have provided the support then we can assume that they did it
129
-    // with the intention to have Picture-in-Picture enabled.
130
-    return self.delegate
131
-        && [self.delegate respondsToSelector:@selector(enterPictureInPicture:)];
107
+    [self setProps:@{}];
132
 }
108
 }
133
 
109
 
134
 #pragma mark Private methods
110
 #pragma mark Private methods
135
 
111
 
136
 /**
112
 /**
137
- * Loads a specific URL which may identify a conference to join. The URL is
138
- * specified in the form of an `NSDictionary` of properties which (1)
139
- * internally are sufficient to construct a URL `NSString` while (2) abstracting
140
- * the specifics of constructing the URL away from API clients/consumers. If the
141
- * specified URL is `nil` and the Welcome page is enabled, the Welcome page is
142
- * displayed instead.
113
+ * Passes the given props to the React Native application. The props which we pass
114
+ * are a combination of 3 different sources:
143
  *
115
  *
144
- * @param urlObject The URL to load which may identify a conference to join.
116
+ * - JitsiMeet.defaultConferenceOptions
117
+ * - This function's parameters
118
+ * - Some extras which are added by this function
145
  */
119
  */
146
-- (void)loadURL:(NSDictionary *_Nullable)urlObject {
147
-    NSMutableDictionary *props = [[NSMutableDictionary alloc] init];
120
+- (void)setProps:(NSDictionary *_Nonnull)newProps {
121
+    NSMutableDictionary *props = mergeProps([[JitsiMeet sharedInstance] getDefaultProps], newProps);
148
 
122
 
149
-    if (self.defaultURL) {
150
-        props[@"defaultURL"] = [self.defaultURL absoluteString];
151
-    }
152
-
153
-    props[@"colorScheme"] = self.colorScheme;
154
     props[@"externalAPIScope"] = externalAPIScope;
123
     props[@"externalAPIScope"] = externalAPIScope;
155
-    props[@"pictureInPictureEnabled"] = @(self.pictureInPictureEnabled);
156
-    props[@"welcomePageEnabled"] = @(self.welcomePageEnabled);
157
 
124
 
158
-    // XXX If urlObject is nil, then it must appear as undefined in the
159
-    // JavaScript source code so that we check the launchOptions there.
160
-    if (urlObject) {
161
-        props[@"url"] = urlObject;
162
-    }
125
+    // TODO: put this in some 'flags' field
126
+    props[@"pictureInPictureEnabled"]
127
+        = @(self.delegate && [self.delegate respondsToSelector:@selector(enterPictureInPicture:)]);
163
 
128
 
164
-    // XXX The method loadURL: is supposed to be imperative i.e. a second
129
+    // This method is supposed to be imperative i.e. a second
165
     // invocation with one and the same URL is expected to join the respective
130
     // invocation with one and the same URL is expected to join the respective
166
     // conference again if the first invocation was followed by leaving the
131
     // conference again if the first invocation was followed by leaving the
167
     // conference. However, React and, respectively,
132
     // conference. However, React and, respectively,
168
     // appProperties/initialProperties are declarative expressions i.e. one and
133
     // appProperties/initialProperties are declarative expressions i.e. one and
169
     // the same URL will not trigger an automatic re-render in the JavaScript
134
     // the same URL will not trigger an automatic re-render in the JavaScript
170
     // source code. The workaround implemented bellow introduces imperativeness
135
     // source code. The workaround implemented bellow introduces imperativeness
171
-    // in React Component props by defining a unique value per loadURL:
172
-    // invocation.
136
+    // in React Component props by defining a unique value per invocation.
173
     props[@"timestamp"] = @(mach_absolute_time());
137
     props[@"timestamp"] = @(mach_absolute_time());
174
 
138
 
175
     if (rootView) {
139
     if (rootView) {
192
     }
156
     }
193
 }
157
 }
194
 
158
 
195
-+ (BOOL)loadURLInViews:(NSDictionary *)urlObject {
159
++ (BOOL)setPropsInViews:(NSDictionary *_Nonnull)newProps {
196
     BOOL handled = NO;
160
     BOOL handled = NO;
197
 
161
 
198
     if (views) {
162
     if (views) {
201
                 = [self viewForExternalAPIScope:externalAPIScope];
165
                 = [self viewForExternalAPIScope:externalAPIScope];
202
 
166
 
203
             if (view) {
167
             if (view) {
204
-                [view loadURL:urlObject];
168
+                [view setProps:newProps];
205
                 handled = YES;
169
                 handled = YES;
206
             }
170
             }
207
         }
171
         }

+ 4
- 3
ios/sdk/src/ReactUtils.h View File

14
  * limitations under the License.
14
  * limitations under the License.
15
  */
15
  */
16
 
16
 
17
-#ifndef ReactUtils_h
18
-#define ReactUtils_h
17
+#ifndef JM_REACTUTILS_H
18
+#define JM_REACTUTILS_H
19
 
19
 
20
+NSMutableDictionary* mergeProps(NSDictionary *a, NSDictionary *b);
20
 void registerReactFatalErrorHandler(void);
21
 void registerReactFatalErrorHandler(void);
21
 
22
 
22
-#endif /* ReactUtils_h */
23
+#endif /* JM_REACTUTILS_H */

+ 40
- 0
ios/sdk/src/ReactUtils.m View File

18
 
18
 
19
 #import "ReactUtils.h"
19
 #import "ReactUtils.h"
20
 
20
 
21
+#pragma mark - Utility functions
22
+
23
+/**
24
+ * Merges 2 sets of props into a single one.
25
+ */
26
+NSMutableDictionary* mergeProps(NSDictionary *a, NSDictionary *b) {
27
+    if (a == nil) {
28
+        return [NSMutableDictionary dictionaryWithDictionary:b == nil ? @{} : b];
29
+    }
30
+
31
+    if (b == nil) {
32
+        return [NSMutableDictionary dictionaryWithDictionary:a];
33
+    }
34
+
35
+    // Both have values, let's merge them, the strategy is to take the value from a first,
36
+    // then override it with the one from b. If the value is a dictionary, merge them
37
+    // recursively. Same goes for arrays.
38
+    NSMutableDictionary *result = [NSMutableDictionary dictionaryWithDictionary:a];
39
+
40
+    for (NSString *key in b) {
41
+        id value = b[key];
42
+        id aValue = result[key];
43
+
44
+        if (aValue == nil) {
45
+            result[key] = value;
46
+            continue;
47
+        }
48
+
49
+        if ([value isKindOfClass:NSArray.class]) {
50
+            result[key] = [aValue arrayByAddingObjectsFromArray:value];
51
+        } else if ([value isKindOfClass:NSDictionary.class]) {
52
+            result[key] = mergeProps(aValue, value);
53
+        } else {
54
+            result[key] = value;
55
+        }
56
+    }
57
+
58
+    return result;
59
+}
60
+
21
 /**
61
 /**
22
  * A `RCTFatalHandler` implementation which swallows JavaScript errors. In the
62
  * A `RCTFatalHandler` implementation which swallows JavaScript errors. In the
23
  * Release configuration, React Native will (intentionally) raise an unhandled
63
  * Release configuration, React Native will (intentionally) raise an unhandled

Loading…
Cancel
Save