|
@@ -20,7 +20,7 @@
|
20
|
20
|
#import <React/RCTLinkingManager.h>
|
21
|
21
|
#import <React/RCTRootView.h>
|
22
|
22
|
|
23
|
|
-#import "JitsiMeetView.h"
|
|
23
|
+#import "JitsiMeetView+Private.h"
|
24
|
24
|
#import "RCTBridgeWrapper.h"
|
25
|
25
|
|
26
|
26
|
/**
|
|
@@ -46,7 +46,61 @@ RCTFatalHandler _RCTFatal = ^(NSError *error) {
|
46
|
46
|
}
|
47
|
47
|
};
|
48
|
48
|
|
|
49
|
+/**
|
|
50
|
+ * Helper function to dynamically load custom fonts. The UIAppFonts key in the
|
|
51
|
+ * plist file doesn't work for frameworks, so fonts have to be manually loaded.
|
|
52
|
+ */
|
|
53
|
+void loadCustomFonts(Class clazz) {
|
|
54
|
+ NSBundle *bundle = [NSBundle bundleForClass:clazz];
|
|
55
|
+ NSArray *fonts = [bundle objectForInfoDictionaryKey:@"JitsiMeetFonts"];
|
|
56
|
+
|
|
57
|
+ for (NSString *item in fonts) {
|
|
58
|
+ NSString *fontName = [item stringByDeletingPathExtension];
|
|
59
|
+ NSString *fontExt = [item pathExtension];
|
|
60
|
+ NSString *fontPath = [bundle pathForResource:fontName ofType:fontExt];
|
|
61
|
+ NSData *inData = [NSData dataWithContentsOfFile:fontPath];
|
|
62
|
+ CFErrorRef error;
|
|
63
|
+ CGDataProviderRef provider
|
|
64
|
+ = CGDataProviderCreateWithCFData((__bridge CFDataRef)inData);
|
|
65
|
+ CGFontRef font = CGFontCreateWithDataProvider(provider);
|
|
66
|
+
|
|
67
|
+ if (!CTFontManagerRegisterGraphicsFont(font, &error)) {
|
|
68
|
+ CFStringRef errorDescription = CFErrorCopyDescription(error);
|
|
69
|
+
|
|
70
|
+ NSLog(@"Failed to load font: %@", errorDescription);
|
|
71
|
+ CFRelease(errorDescription);
|
|
72
|
+ }
|
|
73
|
+ CFRelease(font);
|
|
74
|
+ CFRelease(provider);
|
|
75
|
+ }
|
|
76
|
+}
|
|
77
|
+
|
|
78
|
+/**
|
|
79
|
+ * Helper function to register a fatal error handler for React. Our handler
|
|
80
|
+ * won't kill the process, it will swallow JS errors and print stack traces
|
|
81
|
+ * instead.
|
|
82
|
+ */
|
|
83
|
+void registerFatalErrorHandler() {
|
|
84
|
+#if !DEBUG
|
|
85
|
+ // In the Release configuration, React Native will (intentionally) raise
|
|
86
|
+ // an unhandled NSException for an unhandled JavaScript error. This will
|
|
87
|
+ // effectively kill the application. In accord with the Web, do not kill
|
|
88
|
+ // the application.
|
|
89
|
+ if (!RCTGetFatalHandler()) {
|
|
90
|
+ RCTSetFatalHandler(_RCTFatal);
|
|
91
|
+ }
|
|
92
|
+#endif
|
|
93
|
+}
|
|
94
|
+
|
49
|
95
|
@interface JitsiMeetView() {
|
|
96
|
+ /**
|
|
97
|
+ * The unique identifier of this {@code JitsiMeetView} within the process
|
|
98
|
+ * for the purposes of {@link ExternalAPI}. The name scope was inspired by
|
|
99
|
+ * postis which we use on Web for the similar purposes of the iframe-based
|
|
100
|
+ * external API.
|
|
101
|
+ */
|
|
102
|
+ NSString *externalAPIScope;
|
|
103
|
+
|
50
|
104
|
RCTRootView *rootView;
|
51
|
105
|
}
|
52
|
106
|
|
|
@@ -55,7 +109,12 @@ RCTFatalHandler _RCTFatal = ^(NSError *error) {
|
55
|
109
|
@implementation JitsiMeetView
|
56
|
110
|
|
57
|
111
|
static RCTBridgeWrapper *bridgeWrapper;
|
58
|
|
-static JitsiMeetView *instance;
|
|
112
|
+
|
|
113
|
+/**
|
|
114
|
+ * The {@code JitsiMeetView}s associated with their {@code ExternalAPI} scopes
|
|
115
|
+ * (i.e. unique identifiers within the process).
|
|
116
|
+ */
|
|
117
|
+static NSMapTable<NSString *, JitsiMeetView *> *views;
|
59
|
118
|
|
60
|
119
|
#pragma mark Linking delegate helpers
|
61
|
120
|
// https://facebook.github.io/react-native/docs/linking.html
|
|
@@ -79,12 +138,12 @@ static JitsiMeetView *instance;
|
79
|
138
|
annotation:annotation];
|
80
|
139
|
}
|
81
|
140
|
|
82
|
|
-#pragma mark initializers
|
|
141
|
+#pragma mark Initializers
|
83
|
142
|
|
84
|
143
|
- (instancetype)initWithCoder:(NSCoder *)coder {
|
85
|
144
|
self = [super initWithCoder:coder];
|
86
|
145
|
if (self) {
|
87
|
|
- [self initialize];
|
|
146
|
+ [self initWithXXX];
|
88
|
147
|
}
|
89
|
148
|
|
90
|
149
|
return self;
|
|
@@ -93,7 +152,7 @@ static JitsiMeetView *instance;
|
93
|
152
|
- (instancetype)initWithFrame:(CGRect)frame {
|
94
|
153
|
self = [super initWithFrame:frame];
|
95
|
154
|
if (self) {
|
96
|
|
- [self initialize];
|
|
155
|
+ [self initWithXXX];
|
97
|
156
|
}
|
98
|
157
|
|
99
|
158
|
return self;
|
|
@@ -101,13 +160,15 @@ static JitsiMeetView *instance;
|
101
|
160
|
|
102
|
161
|
#pragma mark API
|
103
|
162
|
|
104
|
|
-/*
|
|
163
|
+/**
|
105
|
164
|
* Loads the given URL and joins the specified conference. If the specified URL
|
106
|
165
|
* is null, the welcome page is shown.
|
107
|
166
|
*/
|
108
|
167
|
- (void)loadURL:(NSURL *)url {
|
109
|
168
|
NSMutableDictionary *props = [[NSMutableDictionary alloc] init];
|
110
|
169
|
|
|
170
|
+ // externalAPIScope
|
|
171
|
+ [props setObject:externalAPIScope forKey:@"externalAPIScope"];
|
111
|
172
|
// url
|
112
|
173
|
if (url) {
|
113
|
174
|
[props setObject:url.absoluteString forKey:@"url"];
|
|
@@ -131,99 +192,44 @@ static JitsiMeetView *instance;
|
131
|
192
|
}
|
132
|
193
|
}
|
133
|
194
|
|
134
|
|
-#pragma mark private methods
|
|
195
|
+#pragma mark Private methods
|
135
|
196
|
|
136
|
|
-+ (instancetype)getInstance {
|
137
|
|
- return instance;
|
|
197
|
++ (instancetype)viewForExternalAPIScope:(NSString *)externalAPIScope {
|
|
198
|
+ return [views objectForKey:externalAPIScope];
|
138
|
199
|
}
|
139
|
200
|
|
140
|
|
-/*
|
|
201
|
+/**
|
141
|
202
|
* Internal initialization:
|
142
|
203
|
*
|
143
|
|
- * - sets the backgroudn color
|
144
|
|
- * - creates the React bridge
|
145
|
|
- * - loads the necessary custom fonts
|
146
|
|
- * - registers a custom fatal error error handler for React
|
|
204
|
+ * - sets the backgroudn color
|
|
205
|
+ * - creates the React bridge
|
|
206
|
+ * - loads the necessary custom fonts
|
|
207
|
+ * - registers a custom fatal error error handler for React
|
147
|
208
|
*/
|
148
|
|
-- (void)initialize {
|
149
|
|
- static dispatch_once_t onceToken;
|
150
|
|
-
|
151
|
|
- /*
|
152
|
|
- * TODO: Only allow a single instance for now. All React Native modules are
|
153
|
|
- * kinda singletons so global state would be broken since we have a single
|
154
|
|
- * bridge. Once we have that sorted out multiple instances of JitsiMeetView
|
155
|
|
- * will be allowed.
|
156
|
|
- */
|
157
|
|
- if (instance != nil) {
|
158
|
|
- @throw [NSException
|
159
|
|
- exceptionWithName:@"RuntimeError"
|
160
|
|
- reason:@"Only a single instance is currently allowed"
|
161
|
|
- userInfo:nil];
|
162
|
|
- }
|
163
|
|
- instance = self;
|
|
209
|
+- (void)initWithXXX {
|
|
210
|
+ static dispatch_once_t dispatchOncePredicate;
|
164
|
211
|
|
165
|
|
- dispatch_once(&onceToken, ^{
|
166
|
|
- // Set a background color which is in accord with the JavaScript and
|
167
|
|
- // Android parts of the application and causes less perceived visual
|
168
|
|
- // flicker than the default background color.
|
169
|
|
- self.backgroundColor
|
170
|
|
- = [UIColor colorWithRed:.07f green:.07f blue:.07f alpha:1];
|
171
|
|
-
|
172
|
|
- // Initialize the React bridge.
|
|
212
|
+ dispatch_once(&dispatchOncePredicate, ^{
|
|
213
|
+ // Initialize the static state of JitsiMeetView.
|
173
|
214
|
bridgeWrapper = [[RCTBridgeWrapper alloc] init];
|
|
215
|
+ views = [NSMapTable strongToWeakObjectsMapTable];
|
174
|
216
|
|
175
|
217
|
// Dynamically load custom bundled fonts.
|
176
|
|
- [self loadCustomFonts];
|
|
218
|
+ loadCustomFonts(self.class);
|
177
|
219
|
|
178
|
220
|
// Register a fatal error handler for React.
|
179
|
|
- [self registerFatalErrorHandler];
|
|
221
|
+ registerFatalErrorHandler();
|
180
|
222
|
});
|
181
|
|
-}
|
182
|
|
-
|
183
|
|
-/*
|
184
|
|
- * Helper function to dynamically load custom fonts. The UIAppFonts key in the
|
185
|
|
- * plist file doesn't work for frameworks, so fonts have to be manually loaded.
|
186
|
|
- */
|
187
|
|
-- (void)loadCustomFonts {
|
188
|
|
- NSBundle *bundle = [NSBundle bundleForClass:self.class];
|
189
|
|
- NSArray *fonts = [bundle objectForInfoDictionaryKey:@"JitsiMeetFonts"];
|
190
|
|
-
|
191
|
|
- for (NSString *item in fonts) {
|
192
|
|
- NSString *fontName = [item stringByDeletingPathExtension];
|
193
|
|
- NSString *fontExt = [item pathExtension];
|
194
|
|
- NSString *fontPath = [bundle pathForResource:fontName ofType:fontExt];
|
195
|
|
- NSData *inData = [NSData dataWithContentsOfFile:fontPath];
|
196
|
|
- CFErrorRef error;
|
197
|
|
- CGDataProviderRef provider
|
198
|
|
- = CGDataProviderCreateWithCFData((__bridge CFDataRef)inData);
|
199
|
|
- CGFontRef font = CGFontCreateWithDataProvider(provider);
|
200
|
223
|
|
201
|
|
- if (!CTFontManagerRegisterGraphicsFont(font, &error)) {
|
202
|
|
- CFStringRef errorDescription = CFErrorCopyDescription(error);
|
203
|
|
-
|
204
|
|
- NSLog(@"Failed to load font: %@", errorDescription);
|
205
|
|
- CFRelease(errorDescription);
|
206
|
|
- }
|
207
|
|
- CFRelease(font);
|
208
|
|
- CFRelease(provider);
|
209
|
|
- }
|
210
|
|
-}
|
|
224
|
+ // Hook this JitsiMeetView into ExternalAPI.
|
|
225
|
+ externalAPIScope = [NSUUID UUID].UUIDString;
|
|
226
|
+ [views setObject:self forKey:externalAPIScope];
|
211
|
227
|
|
212
|
|
-/*
|
213
|
|
- * Helper function to register a fatal error handler for React. Our handler
|
214
|
|
- * won't kill the process, it will swallow JS errors and print stack traces
|
215
|
|
- * instead.
|
216
|
|
- */
|
217
|
|
-- (void)registerFatalErrorHandler {
|
218
|
|
-#if !DEBUG
|
219
|
|
- // In the Release configuration, React Native will (intentionally) raise
|
220
|
|
- // an unhandled NSException for an unhandled JavaScript error. This will
|
221
|
|
- // effectively kill the application. In accord with the Web, do not kill
|
222
|
|
- // the application.
|
223
|
|
- if (!RCTGetFatalHandler()) {
|
224
|
|
- RCTSetFatalHandler(_RCTFatal);
|
225
|
|
- }
|
226
|
|
-#endif
|
|
228
|
+ // Set a background color which is in accord with the JavaScript and
|
|
229
|
+ // Android parts of the application and causes less perceived visual
|
|
230
|
+ // flicker than the default background color.
|
|
231
|
+ self.backgroundColor
|
|
232
|
+ = [UIColor colorWithRed:.07f green:.07f blue:.07f alpha:1];
|
227
|
233
|
}
|
228
|
234
|
|
229
|
235
|
@end
|