Просмотр исходного кода

ios: introduce JitsiMeetConferenceOptions

j8
Saúl Ibarra Corretgé 6 лет назад
Родитель
Сommit
aedcfba263

+ 11
- 4
ios/app/src/AppDelegate.m Просмотреть файл

@@ -37,11 +37,18 @@
37 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 53
     return YES;
47 54
 }

+ 3
- 7
ios/app/src/ViewController.m Просмотреть файл

@@ -21,6 +21,8 @@
21 21
 @import MobileCoreServices;
22 22
 @import Intents;  // Needed for NSUserActivity suggestedInvocationPhrase
23 23
 
24
+@import JitsiMeet;
25
+
24 26
 #import "Types.h"
25 27
 #import "ViewController.h"
26 28
 
@@ -33,13 +35,7 @@
33 35
     JitsiMeetView *view = (JitsiMeetView *) self.view;
34 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 41
 // JitsiMeetViewDelegate

+ 10
- 2
ios/sdk/sdk.xcodeproj/project.pbxproj Просмотреть файл

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

+ 1
- 0
ios/sdk/src/JitsiMeet+Private.h Просмотреть файл

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

+ 4
- 1
ios/sdk/src/JitsiMeet.h Просмотреть файл

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

+ 30
- 15
ios/sdk/src/JitsiMeet.m Просмотреть файл

@@ -18,6 +18,7 @@
18 18
 
19 19
 #import "Dropbox.h"
20 20
 #import "JitsiMeet+Private.h"
21
+#import "JitsiMeetConferenceOptions+Private.h"
21 22
 #import "JitsiMeetView+Private.h"
22 23
 #import "RCTBridgeWrapper.h"
23 24
 #import "ReactUtils.h"
@@ -71,9 +72,9 @@
71 72
   continueUserActivity:(NSUserActivity *)userActivity
72 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 80
 - (BOOL)application:(UIApplication *)app
@@ -95,36 +96,44 @@
95 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 106
 #pragma mark - Utility methods
102 107
 
103
-- (NSDictionary *)getInitialURL {
108
+- (JitsiMeetConferenceOptions *)getInitialConferenceOptions {
104 109
     if (_launchOptions[UIApplicationLaunchOptionsURLKey]) {
105 110
         NSURL *url = _launchOptions[UIApplicationLaunchOptionsURLKey];
106
-        return @{ @"url" : url.absoluteString };
111
+        return [JitsiMeetConferenceOptions fromBuilder:^(JitsiMeetConferenceOptionsBuilder *builder) {
112
+            builder.room = [url absoluteString];
113
+        }];
107 114
     } else {
108 115
         NSDictionary *userActivityDictionary
109 116
             = _launchOptions[UIApplicationLaunchOptionsUserActivityDictionaryKey];
110 117
         NSUserActivity *userActivity
111 118
             = [userActivityDictionary objectForKey:@"UIApplicationLaunchOptionsUserActivityKey"];
112 119
         if (userActivity != nil) {
113
-            return [self urlFromUserActivity:userActivity];
120
+            return [self optionsFromUserActivity:userActivity];
114 121
         }
115 122
     }
116 123
 
117 124
     return nil;
118 125
 }
119 126
 
120
-- (NSDictionary *)urlFromUserActivity:(NSUserActivity *)userActivity {
127
+- (JitsiMeetConferenceOptions *)optionsFromUserActivity:(NSUserActivity *)userActivity {
121 128
     NSString *activityType = userActivity.activityType;
122 129
 
123 130
     if ([activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
124 131
         // App was started by opening a URL in the browser
125 132
         NSURL *url = userActivity.webpageURL;
126 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 138
     } else if ([activityType isEqualToString:@"INStartAudioCallIntent"]
130 139
                || [activityType isEqualToString:@"INStartVideoCallIntent"]) {
@@ -132,27 +141,29 @@
132 141
         INIntent *intent = userActivity.interaction.intent;
133 142
         NSArray<INPerson *> *contacts;
134 143
         NSString *url;
135
-        BOOL startAudioOnly = NO;
144
+        BOOL audioOnly = NO;
136 145
 
137 146
         if ([intent isKindOfClass:[INStartAudioCallIntent class]]) {
138 147
             contacts = ((INStartAudioCallIntent *) intent).contacts;
139
-            startAudioOnly = YES;
148
+            audioOnly = YES;
140 149
         } else if ([intent isKindOfClass:[INStartVideoCallIntent class]]) {
141 150
             contacts = ((INStartVideoCallIntent *) intent).contacts;
142 151
         }
143 152
 
144 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 159
     } else if (self.conferenceActivityType && [activityType isEqualToString:self.conferenceActivityType]) {
151 160
         // App was started by continuing a registered NSUserActivity (SiriKit, Handoff, ...)
152 161
         NSString *url;
153 162
 
154 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,6 +182,10 @@
171 182
 
172 183
 #pragma mark - Private API methods
173 184
 
185
+- (NSDictionary *)getDefaultProps {
186
+    return _defaultConferenceOptions == nil ? @{} : [_defaultConferenceOptions asProps];
187
+}
188
+
174 189
 - (RCTBridge *)getReactBridge {
175 190
     return _bridgeWrapper.bridge;
176 191
 }

+ 23
- 0
ios/sdk/src/JitsiMeetConferenceOptions+Private.h Просмотреть файл

@@ -0,0 +1,23 @@
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 Просмотреть файл

@@ -0,0 +1,52 @@
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 Просмотреть файл

@@ -0,0 +1,212 @@
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 Просмотреть файл

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

+ 2
- 11
ios/sdk/src/JitsiMeetView.h Просмотреть файл

@@ -18,23 +18,14 @@
18 18
 #import <Foundation/Foundation.h>
19 19
 #import <UIKit/UIKit.h>
20 20
 
21
+#import "JitsiMeetConferenceOptions.h"
21 22
 #import "JitsiMeetViewDelegate.h"
22 23
 
23 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 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 29
 - (void)leave;
39 30
 
40 31
 @end

+ 20
- 56
ios/sdk/src/JitsiMeetView.m Просмотреть файл

@@ -20,7 +20,9 @@
20 20
 #import <React/RCTRootView.h>
21 21
 
22 22
 #import "JitsiMeet+Private.h"
23
+#import "JitsiMeetConferenceOptions+Private.h"
23 24
 #import "JitsiMeetView+Private.h"
25
+#import "ReactUtils.h"
24 26
 
25 27
 
26 28
 @implementation JitsiMeetView {
@@ -32,12 +34,8 @@
32 34
     NSString *externalAPIScope;
33 35
 
34 36
     RCTRootView *rootView;
35
-
36
-    NSNumber *_pictureInPictureEnabled;
37 37
 }
38 38
 
39
-@dynamic pictureInPictureEnabled;
40
-
41 39
 /**
42 40
  * The `JitsiMeetView`s associated with their `ExternalAPI` scopes (i.e. unique
43 41
  * identifiers within the process).
@@ -96,80 +94,46 @@ static void initializeViewsMap() {
96 94
     // parts of the application and causes less perceived visual flicker than
97 95
     // the default background color.
98 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 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 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 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 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 130
     // invocation with one and the same URL is expected to join the respective
166 131
     // conference again if the first invocation was followed by leaving the
167 132
     // conference. However, React and, respectively,
168 133
     // appProperties/initialProperties are declarative expressions i.e. one and
169 134
     // the same URL will not trigger an automatic re-render in the JavaScript
170 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 137
     props[@"timestamp"] = @(mach_absolute_time());
174 138
 
175 139
     if (rootView) {
@@ -192,7 +156,7 @@ static void initializeViewsMap() {
192 156
     }
193 157
 }
194 158
 
195
-+ (BOOL)loadURLInViews:(NSDictionary *)urlObject {
159
++ (BOOL)setPropsInViews:(NSDictionary *_Nonnull)newProps {
196 160
     BOOL handled = NO;
197 161
 
198 162
     if (views) {
@@ -201,7 +165,7 @@ static void initializeViewsMap() {
201 165
                 = [self viewForExternalAPIScope:externalAPIScope];
202 166
 
203 167
             if (view) {
204
-                [view loadURL:urlObject];
168
+                [view setProps:newProps];
205 169
                 handled = YES;
206 170
             }
207 171
         }

+ 4
- 3
ios/sdk/src/ReactUtils.h Просмотреть файл

@@ -14,9 +14,10 @@
14 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 21
 void registerReactFatalErrorHandler(void);
21 22
 
22
-#endif /* ReactUtils_h */
23
+#endif /* JM_REACTUTILS_H */

+ 40
- 0
ios/sdk/src/ReactUtils.m Просмотреть файл

@@ -18,6 +18,46 @@
18 18
 
19 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 62
  * A `RCTFatalHandler` implementation which swallows JavaScript errors. In the
23 63
  * Release configuration, React Native will (intentionally) raise an unhandled

Загрузка…
Отмена
Сохранить