@haseeba393/react-native-zendesk 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +171 -0
- package/android/build.gradle +55 -0
- package/android/src/main/AndroidManifest.xml +1 -0
- package/android/src/main/java/com/rnzendesk/ZendeskHelpCenterViewManager.kt +38 -0
- package/android/src/main/java/com/rnzendesk/ZendeskModule.kt +341 -0
- package/android/src/main/java/com/rnzendesk/ZendeskPackage.kt +59 -0
- package/ios/RNZendeskHelpCenterView.h +9 -0
- package/ios/RNZendeskHelpCenterView.m +43 -0
- package/ios/RNZendeskHelpCenterViewManager.m +19 -0
- package/ios/RNZendeskModule.h +13 -0
- package/ios/RNZendeskModule.mm +515 -0
- package/package.json +63 -0
- package/react-native-zendesk.podspec +38 -0
- package/src/ZendeskHelpCenterView.tsx +13 -0
- package/src/fabric/ZendeskHelpCenterViewNativeComponent.ts +13 -0
- package/src/index.ts +57 -0
- package/src/specs/NativeZendesk.ts +53 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#import "RNZendeskHelpCenterView.h"
|
|
2
|
+
|
|
3
|
+
@interface RNZendeskHelpCenterView ()
|
|
4
|
+
@property(nonatomic, strong) WKWebView *webView;
|
|
5
|
+
@end
|
|
6
|
+
|
|
7
|
+
@implementation RNZendeskHelpCenterView
|
|
8
|
+
|
|
9
|
+
- (instancetype)initWithFrame:(CGRect)frame
|
|
10
|
+
{
|
|
11
|
+
if (self = [super initWithFrame:frame]) {
|
|
12
|
+
_javaScriptEnabled = YES;
|
|
13
|
+
WKWebViewConfiguration *config = [WKWebViewConfiguration new];
|
|
14
|
+
config.defaultWebpagePreferences.allowsContentJavaScript = YES;
|
|
15
|
+
_webView = [[WKWebView alloc] initWithFrame:self.bounds configuration:config];
|
|
16
|
+
_webView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
|
|
17
|
+
[self addSubview:_webView];
|
|
18
|
+
}
|
|
19
|
+
return self;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
- (void)setJavaScriptEnabled:(BOOL)javaScriptEnabled
|
|
23
|
+
{
|
|
24
|
+
_javaScriptEnabled = javaScriptEnabled;
|
|
25
|
+
self.webView.configuration.defaultWebpagePreferences.allowsContentJavaScript = javaScriptEnabled;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
- (void)setUrl:(NSString *)url
|
|
29
|
+
{
|
|
30
|
+
_url = [url copy];
|
|
31
|
+
if (_url.length == 0) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
NSURL *targetURL = [NSURL URLWithString:_url];
|
|
36
|
+
if (targetURL == nil) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
NSURLRequest *request = [NSURLRequest requestWithURL:targetURL];
|
|
40
|
+
[self.webView loadRequest:request];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
@end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#import <React/RCTViewManager.h>
|
|
2
|
+
#import "RNZendeskHelpCenterView.h"
|
|
3
|
+
|
|
4
|
+
@interface RNZendeskHelpCenterViewManager : RCTViewManager
|
|
5
|
+
@end
|
|
6
|
+
|
|
7
|
+
@implementation RNZendeskHelpCenterViewManager
|
|
8
|
+
|
|
9
|
+
RCT_EXPORT_MODULE(RNZendeskHelpCenterView)
|
|
10
|
+
|
|
11
|
+
- (UIView *)view
|
|
12
|
+
{
|
|
13
|
+
return [RNZendeskHelpCenterView new];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
RCT_EXPORT_VIEW_PROPERTY(url, NSString)
|
|
17
|
+
RCT_EXPORT_VIEW_PROPERTY(javaScriptEnabled, BOOL)
|
|
18
|
+
|
|
19
|
+
@end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#import <React/RCTBridgeModule.h>
|
|
2
|
+
#import <React/RCTEventEmitter.h>
|
|
3
|
+
|
|
4
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
5
|
+
#import <ReactCommon/RCTTurboModule.h>
|
|
6
|
+
#endif
|
|
7
|
+
|
|
8
|
+
@interface RNZendeskModule : NSObject <RCTBridgeModule
|
|
9
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
10
|
+
, RCTTurboModule
|
|
11
|
+
#endif
|
|
12
|
+
>
|
|
13
|
+
@end
|
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
#import "RNZendeskModule.h"
|
|
2
|
+
|
|
3
|
+
#import <React/RCTConvert.h>
|
|
4
|
+
#import <React/RCTLog.h>
|
|
5
|
+
#import <UIKit/UIKit.h>
|
|
6
|
+
#import <SupportSDK/SupportSDK.h>
|
|
7
|
+
#import <ZendeskCoreSDK/ZendeskCoreSDK.h>
|
|
8
|
+
|
|
9
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
10
|
+
#import <ReactCommon/CallInvoker.h>
|
|
11
|
+
#import <ReactCommon/TurboModule.h>
|
|
12
|
+
#if __has_include(<React-Codegen/RNZendeskSpec.h>)
|
|
13
|
+
#import <React-Codegen/RNZendeskSpec.h>
|
|
14
|
+
#define RN_ZENDESK_HAS_CODEGEN 1
|
|
15
|
+
#elif __has_include("RNZendeskSpec.h")
|
|
16
|
+
#import "RNZendeskSpec.h"
|
|
17
|
+
#define RN_ZENDESK_HAS_CODEGEN 1
|
|
18
|
+
#else
|
|
19
|
+
#define RN_ZENDESK_HAS_CODEGEN 0
|
|
20
|
+
#endif
|
|
21
|
+
#endif
|
|
22
|
+
|
|
23
|
+
@interface RNZendeskModule ()
|
|
24
|
+
@property(nonatomic, strong) NSString *subdomain;
|
|
25
|
+
@property(nonatomic, strong) NSString *email;
|
|
26
|
+
@property(nonatomic, strong) NSString *apiToken;
|
|
27
|
+
@property(nonatomic, strong) NSString *locale;
|
|
28
|
+
@property(nonatomic, assign) BOOL sdkInitialized;
|
|
29
|
+
@end
|
|
30
|
+
|
|
31
|
+
@implementation RNZendeskModule
|
|
32
|
+
|
|
33
|
+
RCT_EXPORT_MODULE(RNZendesk)
|
|
34
|
+
|
|
35
|
+
+ (BOOL)requiresMainQueueSetup
|
|
36
|
+
{
|
|
37
|
+
return NO;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
- (instancetype)init
|
|
41
|
+
{
|
|
42
|
+
if (self = [super init]) {
|
|
43
|
+
_locale = @"";
|
|
44
|
+
_sdkInitialized = NO;
|
|
45
|
+
}
|
|
46
|
+
return self;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
RCT_EXPORT_METHOD(initialize
|
|
50
|
+
: (NSDictionary *)config resolve
|
|
51
|
+
: (RCTPromiseResolveBlock)resolve reject
|
|
52
|
+
: (RCTPromiseRejectBlock)reject)
|
|
53
|
+
{
|
|
54
|
+
NSString *providedSubdomain = [RCTConvert NSString:config[@"subdomain"]];
|
|
55
|
+
self.email = [RCTConvert NSString:config[@"email"]];
|
|
56
|
+
self.apiToken = [RCTConvert NSString:config[@"apiToken"]];
|
|
57
|
+
NSString *name = [RCTConvert NSString:config[@"name"]];
|
|
58
|
+
NSString *locale = [RCTConvert NSString:config[@"locale"]];
|
|
59
|
+
self.locale = locale.length > 0 ? locale : @"";
|
|
60
|
+
|
|
61
|
+
NSString *zendeskUrl = [RCTConvert NSString:config[@"zendeskUrl"]];
|
|
62
|
+
NSString *appId = [RCTConvert NSString:config[@"appId"]];
|
|
63
|
+
NSString *clientId = [RCTConvert NSString:config[@"clientId"]];
|
|
64
|
+
NSString *derivedSubdomain = [self extractSubdomainFromZendeskUrl:zendeskUrl];
|
|
65
|
+
self.subdomain = providedSubdomain.length > 0 ? providedSubdomain : derivedSubdomain;
|
|
66
|
+
|
|
67
|
+
if (self.subdomain.length == 0) {
|
|
68
|
+
reject(@"E_ZENDESK_CONFIG",
|
|
69
|
+
@"Provide subdomain or a valid zendeskUrl (https://<subdomain>.zendesk.com)",
|
|
70
|
+
nil);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
self.sdkInitialized = NO;
|
|
75
|
+
if (zendeskUrl.length > 0 && appId.length > 0 && clientId.length > 0) {
|
|
76
|
+
@try {
|
|
77
|
+
[ZDKClassicZendesk initializeWithAppId:appId clientId:clientId zendeskUrl:zendeskUrl];
|
|
78
|
+
[ZDKSupport initializeWithZendesk:[ZDKClassicZendesk instance]];
|
|
79
|
+
|
|
80
|
+
id<ZDKObjCIdentity> identity = [[ZDKObjCAnonymous alloc] initWithName:name email:self.email];
|
|
81
|
+
[[ZDKClassicZendesk instance] setIdentity:identity];
|
|
82
|
+
if (self.locale.length > 0) {
|
|
83
|
+
[ZDKSupport instance].helpCenterLocaleOverride = self.locale.lowercaseString;
|
|
84
|
+
}
|
|
85
|
+
self.sdkInitialized = YES;
|
|
86
|
+
} @catch (NSException *exception) {
|
|
87
|
+
reject(@"E_ZENDESK_SDK_INIT", exception.reason, nil);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
resolve(@(YES));
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
RCT_EXPORT_METHOD(getArticles
|
|
95
|
+
: (NSString *)locale labels
|
|
96
|
+
: (NSArray<NSString *> *)labels page
|
|
97
|
+
: (NSNumber *)page perPage
|
|
98
|
+
: (NSNumber *)perPage resolve
|
|
99
|
+
: (RCTPromiseResolveBlock)resolve reject
|
|
100
|
+
: (RCTPromiseRejectBlock)reject)
|
|
101
|
+
{
|
|
102
|
+
if (!resolve || !reject) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
106
|
+
[self openHelpCenterImpl:resolve reject:reject];
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
RCT_EXPORT_METHOD(getArticle
|
|
111
|
+
: (NSNumber *)articleId locale
|
|
112
|
+
: (NSString *)locale resolve
|
|
113
|
+
: (RCTPromiseResolveBlock)resolve reject
|
|
114
|
+
: (RCTPromiseRejectBlock)reject)
|
|
115
|
+
{
|
|
116
|
+
if (!resolve || !reject || !articleId) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
NSNumber *articleIdCopy = [articleId copy];
|
|
120
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
121
|
+
[self openArticleImpl:articleIdCopy resolve:resolve reject:reject];
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
RCT_EXPORT_METHOD(searchArticles
|
|
126
|
+
: (NSString *)query locale
|
|
127
|
+
: (NSString *)locale page
|
|
128
|
+
: (NSNumber *)page perPage
|
|
129
|
+
: (NSNumber *)perPage resolve
|
|
130
|
+
: (RCTPromiseResolveBlock)resolve reject
|
|
131
|
+
: (RCTPromiseRejectBlock)reject)
|
|
132
|
+
{
|
|
133
|
+
if (!resolve || !reject) {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
137
|
+
[self openHelpCenterImpl:resolve reject:reject];
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
RCT_EXPORT_METHOD(createTicket
|
|
142
|
+
: (NSDictionary *)request resolve
|
|
143
|
+
: (RCTPromiseResolveBlock)resolve reject
|
|
144
|
+
: (RCTPromiseRejectBlock)reject)
|
|
145
|
+
{
|
|
146
|
+
if (!resolve || !reject) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
150
|
+
[self openContactSupportImpl:resolve reject:reject];
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
RCT_EXPORT_METHOD(openHelpCenter
|
|
155
|
+
: (RCTPromiseResolveBlock)resolve reject
|
|
156
|
+
: (RCTPromiseRejectBlock)reject)
|
|
157
|
+
{
|
|
158
|
+
if (!resolve || !reject) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
162
|
+
[self openHelpCenterImpl:resolve reject:reject];
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
- (void)openHelpCenterImpl:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject
|
|
167
|
+
{
|
|
168
|
+
if (self.sdkInitialized) {
|
|
169
|
+
UIViewController *helpCenter = [ZDKHelpCenterUi buildHelpCenterOverviewUiWithConfigs:@[]];
|
|
170
|
+
[self presentZendeskController:helpCenter
|
|
171
|
+
resolve:resolve
|
|
172
|
+
reject:reject
|
|
173
|
+
errorCode:@"E_ZENDESK_OPEN_HELP_CENTER"];
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
NSString *domain = [self requiredSubdomainOrReject:reject];
|
|
178
|
+
if (domain.length == 0) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
NSString *localeSegment = self.locale.length > 0 ? [@"/" stringByAppendingString:self.locale] : @"";
|
|
182
|
+
NSString *url = [NSString stringWithFormat:@"https://%@.zendesk.com/hc%@", domain, localeSegment];
|
|
183
|
+
[self openUrl:url resolve:resolve reject:reject];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
RCT_EXPORT_METHOD(openArticle
|
|
187
|
+
: (NSNumber *)articleId resolve
|
|
188
|
+
: (RCTPromiseResolveBlock)resolve reject
|
|
189
|
+
: (RCTPromiseRejectBlock)reject)
|
|
190
|
+
{
|
|
191
|
+
if (!resolve || !reject || !articleId) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
NSNumber *articleIdCopy = [articleId copy];
|
|
195
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
196
|
+
[self openArticleImpl:articleIdCopy resolve:resolve reject:reject];
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
- (void)openArticleImpl:(NSNumber *)articleId resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject
|
|
201
|
+
{
|
|
202
|
+
if (self.sdkInitialized) {
|
|
203
|
+
UIViewController *articleController = [ZDKHelpCenterUi buildHelpCenterArticleUiWithArticleId:articleId.stringValue andConfigs:@[]];
|
|
204
|
+
[self presentZendeskController:articleController
|
|
205
|
+
resolve:resolve
|
|
206
|
+
reject:reject
|
|
207
|
+
errorCode:@"E_ZENDESK_OPEN_ARTICLE"];
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
NSString *domain = [self requiredSubdomainOrReject:reject];
|
|
212
|
+
if (domain.length == 0) {
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
NSString *url = [NSString
|
|
216
|
+
stringWithFormat:@"https://%@.zendesk.com/hc/articles/%@",
|
|
217
|
+
domain, articleId];
|
|
218
|
+
[self openUrl:url resolve:resolve reject:reject];
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
- (void)openContactSupportImpl:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject
|
|
222
|
+
{
|
|
223
|
+
if (self.sdkInitialized) {
|
|
224
|
+
UIViewController *requestController = [ZDKRequestUi buildRequestUiWith:@[]];
|
|
225
|
+
[self presentZendeskController:requestController
|
|
226
|
+
resolve:resolve
|
|
227
|
+
reject:reject
|
|
228
|
+
errorCode:@"E_ZENDESK_OPEN_CONTACT_SUPPORT"];
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
NSString *domain = [self requiredSubdomainOrReject:reject];
|
|
233
|
+
if (domain.length == 0) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
NSString *localeSegment = self.locale.length > 0 ? [@"/" stringByAppendingString:self.locale] : @"";
|
|
237
|
+
NSString *url = [NSString stringWithFormat:@"https://%@.zendesk.com/hc%@/requests/new", domain, localeSegment];
|
|
238
|
+
[self openUrl:url resolve:resolve reject:reject];
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
RCT_EXPORT_METHOD(openContactSupport
|
|
242
|
+
: (RCTPromiseResolveBlock)resolve reject
|
|
243
|
+
: (RCTPromiseRejectBlock)reject)
|
|
244
|
+
{
|
|
245
|
+
if (!resolve || !reject) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
249
|
+
[self openContactSupportImpl:resolve reject:reject];
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
RCT_EXPORT_METHOD(openContactSupportWithDetails
|
|
254
|
+
: (NSString *)email customFields
|
|
255
|
+
: (NSArray<NSDictionary *> *)customFields resolve
|
|
256
|
+
: (RCTPromiseResolveBlock)resolve reject
|
|
257
|
+
: (RCTPromiseRejectBlock)reject)
|
|
258
|
+
{
|
|
259
|
+
if (!resolve || !reject) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
NSString *emailCopy = email ? [email copy] : nil;
|
|
263
|
+
NSArray *customFieldsCopy = customFields ? [customFields copy] : nil;
|
|
264
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
265
|
+
[self openContactSupportWithDetailsImpl:emailCopy customFields:customFieldsCopy resolve:resolve reject:reject];
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
- (void)openContactSupportWithDetailsImpl:(NSString *)email
|
|
270
|
+
customFields:(NSArray<NSDictionary *> *)customFields
|
|
271
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
272
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
273
|
+
{
|
|
274
|
+
if (self.sdkInitialized) {
|
|
275
|
+
if (email.length > 0) {
|
|
276
|
+
id<ZDKObjCIdentity> identity = [[ZDKObjCAnonymous alloc] initWithName:nil email:email];
|
|
277
|
+
[[ZDKClassicZendesk instance] setIdentity:identity];
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
NSMutableArray<ZDKCustomField *> *fields = [NSMutableArray array];
|
|
281
|
+
for (NSDictionary *item in customFields) {
|
|
282
|
+
NSString *key = [RCTConvert NSString:item[@"key"]];
|
|
283
|
+
NSString *value = [RCTConvert NSString:item[@"value"]];
|
|
284
|
+
if (key.length == 0 || value.length == 0) {
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
NSString *keyTrimmed = [key stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
|
|
288
|
+
if ([keyTrimmed hasSuffix:@"L"] || [keyTrimmed hasSuffix:@"l"]) {
|
|
289
|
+
keyTrimmed = [keyTrimmed substringToIndex:keyTrimmed.length - 1];
|
|
290
|
+
}
|
|
291
|
+
NSNumber *fieldId = @(keyTrimmed.longLongValue);
|
|
292
|
+
[fields addObject:[[ZDKCustomField alloc] initWithFieldId:fieldId value:value]];
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
ZDKRequestUiConfiguration *config = [ZDKRequestUiConfiguration new];
|
|
296
|
+
config.customFields = fields;
|
|
297
|
+
|
|
298
|
+
UIViewController *requestController = [ZDKRequestUi buildRequestUiWith:@[ config ]];
|
|
299
|
+
[self presentZendeskController:requestController
|
|
300
|
+
resolve:resolve
|
|
301
|
+
reject:reject
|
|
302
|
+
errorCode:@"E_ZENDESK_OPEN_CONTACT_SUPPORT"];
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
[self openContactSupportImpl:resolve reject:reject];
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
- (void)openUrl:(NSString *)urlString
|
|
310
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
311
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
312
|
+
{
|
|
313
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
314
|
+
NSURL *url = [NSURL URLWithString:urlString];
|
|
315
|
+
if (url == nil) {
|
|
316
|
+
reject(@"E_ZENDESK_OPEN_URL", @"Invalid URL", nil);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (![[UIApplication sharedApplication] canOpenURL:url]) {
|
|
321
|
+
reject(@"E_ZENDESK_OPEN_URL", @"Unable to open URL", nil);
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
[[UIApplication sharedApplication]
|
|
326
|
+
openURL:url
|
|
327
|
+
options:@{}
|
|
328
|
+
completionHandler:^(BOOL success) {
|
|
329
|
+
if (!success) {
|
|
330
|
+
reject(@"E_ZENDESK_OPEN_URL", @"openURL completion failed", nil);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
resolve(@(YES));
|
|
334
|
+
}];
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
- (void)performRequest:(NSString *)method
|
|
339
|
+
url:(NSString *)urlString
|
|
340
|
+
payload:(NSDictionary *)payload
|
|
341
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
342
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
343
|
+
{
|
|
344
|
+
NSURL *url = [NSURL URLWithString:urlString];
|
|
345
|
+
if (url == nil) {
|
|
346
|
+
reject(@"E_ZENDESK_URL", @"Invalid URL", nil);
|
|
347
|
+
return;
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
|
|
351
|
+
request.HTTPMethod = method;
|
|
352
|
+
[request addValue:@"application/json" forHTTPHeaderField:@"Accept"];
|
|
353
|
+
NSString *auth = [self authHeader];
|
|
354
|
+
if (auth.length > 0) {
|
|
355
|
+
[request addValue:auth forHTTPHeaderField:@"Authorization"];
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
if (payload != nil) {
|
|
359
|
+
NSError *serializationError = nil;
|
|
360
|
+
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:payload options:0 error:&serializationError];
|
|
361
|
+
if (serializationError != nil) {
|
|
362
|
+
reject(@"E_ZENDESK_SERIALIZE", serializationError.localizedDescription, serializationError);
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
request.HTTPBody = jsonData;
|
|
366
|
+
[request addValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
NSURLSessionDataTask *task = [[NSURLSession sharedSession]
|
|
370
|
+
dataTaskWithRequest:request
|
|
371
|
+
completionHandler:^(NSData *_Nullable data, NSURLResponse *_Nullable response, NSError *_Nullable error) {
|
|
372
|
+
if (error != nil) {
|
|
373
|
+
reject(@"E_ZENDESK_NETWORK", error.localizedDescription, error);
|
|
374
|
+
return;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
|
|
378
|
+
NSString *rawBody = data != nil ? [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding] : @"{}";
|
|
379
|
+
|
|
380
|
+
if (httpResponse.statusCode < 200 || httpResponse.statusCode >= 300) {
|
|
381
|
+
reject(@"E_ZENDESK_HTTP",
|
|
382
|
+
[NSString stringWithFormat:@"Zendesk request failed (%ld): %@", (long)httpResponse.statusCode, rawBody],
|
|
383
|
+
nil);
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
if (data == nil) {
|
|
388
|
+
resolve(@{});
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
NSError *jsonError = nil;
|
|
393
|
+
id object = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:&jsonError];
|
|
394
|
+
if (jsonError != nil || ![object isKindOfClass:[NSDictionary class]]) {
|
|
395
|
+
resolve(@{ @"raw" : rawBody ?: @"" });
|
|
396
|
+
return;
|
|
397
|
+
}
|
|
398
|
+
resolve(object);
|
|
399
|
+
}];
|
|
400
|
+
[task resume];
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
- (void)presentZendeskController:(UIViewController *)controller
|
|
404
|
+
resolve:(RCTPromiseResolveBlock)resolve
|
|
405
|
+
reject:(RCTPromiseRejectBlock)reject
|
|
406
|
+
errorCode:(NSString *)errorCode
|
|
407
|
+
{
|
|
408
|
+
dispatch_async(dispatch_get_main_queue(), ^{
|
|
409
|
+
UIViewController *top = [self topMostViewController];
|
|
410
|
+
if (top == nil || controller == nil) {
|
|
411
|
+
reject(errorCode, @"Unable to find active view controller", nil);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (top.navigationController != nil) {
|
|
416
|
+
[top.navigationController pushViewController:controller animated:YES];
|
|
417
|
+
} else {
|
|
418
|
+
UINavigationController *nav = [[UINavigationController alloc] initWithRootViewController:controller];
|
|
419
|
+
[top presentViewController:nav animated:YES completion:nil];
|
|
420
|
+
}
|
|
421
|
+
resolve(@(YES));
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
- (UIViewController *)topMostViewController
|
|
426
|
+
{
|
|
427
|
+
UIWindow *window = nil;
|
|
428
|
+
for (UIWindowScene *scene in UIApplication.sharedApplication.connectedScenes) {
|
|
429
|
+
if (![scene isKindOfClass:[UIWindowScene class]]) {
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
if (scene.activationState != UISceneActivationStateForegroundActive) {
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
for (UIWindow *candidate in ((UIWindowScene *)scene).windows) {
|
|
436
|
+
if (candidate.isKeyWindow) {
|
|
437
|
+
window = candidate;
|
|
438
|
+
break;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
if (window != nil) {
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
if (window == nil) {
|
|
446
|
+
window = UIApplication.sharedApplication.keyWindow;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
UIViewController *top = window.rootViewController;
|
|
450
|
+
while (top.presentedViewController != nil) {
|
|
451
|
+
top = top.presentedViewController;
|
|
452
|
+
}
|
|
453
|
+
return top;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
- (NSString *)extractSubdomainFromZendeskUrl:(NSString *)zendeskUrl
|
|
457
|
+
{
|
|
458
|
+
if (zendeskUrl.length == 0) {
|
|
459
|
+
return nil;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
NSURL *url = [NSURL URLWithString:zendeskUrl];
|
|
463
|
+
NSString *host = url.host.lowercaseString;
|
|
464
|
+
NSString *suffix = @".zendesk.com";
|
|
465
|
+
if (host.length == 0 || ![host hasSuffix:suffix]) {
|
|
466
|
+
return nil;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
NSString *subdomain = [host substringToIndex:(host.length - suffix.length)];
|
|
470
|
+
return subdomain.length > 0 ? subdomain : nil;
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
- (NSString *)baseApiUrlOrReject:(RCTPromiseRejectBlock)reject
|
|
474
|
+
{
|
|
475
|
+
NSString *domain = [self requiredSubdomainOrReject:reject];
|
|
476
|
+
if (domain.length == 0) {
|
|
477
|
+
return nil;
|
|
478
|
+
}
|
|
479
|
+
return [NSString stringWithFormat:@"https://%@.zendesk.com/api/v2", domain];
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
- (NSString *)requiredSubdomainOrReject:(RCTPromiseRejectBlock)reject
|
|
483
|
+
{
|
|
484
|
+
if (self.subdomain.length == 0) {
|
|
485
|
+
reject(@"E_ZENDESK_CONFIG", @"Zendesk is not initialized. Call initialize() first.", nil);
|
|
486
|
+
return nil;
|
|
487
|
+
}
|
|
488
|
+
return self.subdomain;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
- (NSString *)authHeader
|
|
492
|
+
{
|
|
493
|
+
if (self.email.length == 0 || self.apiToken.length == 0) {
|
|
494
|
+
return nil;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
NSString *raw = [NSString stringWithFormat:@"%@/token:%@", self.email, self.apiToken];
|
|
498
|
+
NSData *data = [raw dataUsingEncoding:NSUTF8StringEncoding];
|
|
499
|
+
NSString *encoded = [data base64EncodedStringWithOptions:0];
|
|
500
|
+
return [NSString stringWithFormat:@"Basic %@", encoded];
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
#ifdef RCT_NEW_ARCH_ENABLED
|
|
504
|
+
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
505
|
+
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
506
|
+
{
|
|
507
|
+
#if RN_ZENDESK_HAS_CODEGEN
|
|
508
|
+
return std::make_shared<facebook::react::NativeZendeskSpecJSI>(params);
|
|
509
|
+
#else
|
|
510
|
+
return nullptr;
|
|
511
|
+
#endif
|
|
512
|
+
}
|
|
513
|
+
#endif
|
|
514
|
+
|
|
515
|
+
@end
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@haseeba393/react-native-zendesk",
|
|
3
|
+
"version": "2.2.0",
|
|
4
|
+
"description": "React Native Zendesk integration with New Architecture support (TurboModules + Fabric component).",
|
|
5
|
+
"main": "src/index.ts",
|
|
6
|
+
"module": "src/index.ts",
|
|
7
|
+
"types": "src/index.ts",
|
|
8
|
+
"react-native": "src/index",
|
|
9
|
+
"source": "src/index",
|
|
10
|
+
"files": [
|
|
11
|
+
"src",
|
|
12
|
+
"android",
|
|
13
|
+
"ios",
|
|
14
|
+
"react-native-zendesk.podspec",
|
|
15
|
+
"README.md",
|
|
16
|
+
"LICENSE"
|
|
17
|
+
],
|
|
18
|
+
"scripts": {
|
|
19
|
+
"build": "tsc -p tsconfig.json",
|
|
20
|
+
"clean": "rm -rf lib"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"react-native",
|
|
24
|
+
"zendesk",
|
|
25
|
+
"help-center",
|
|
26
|
+
"support",
|
|
27
|
+
"turbo-module",
|
|
28
|
+
"fabric",
|
|
29
|
+
"new-architecture"
|
|
30
|
+
],
|
|
31
|
+
"author": "",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "https://github.com/Haseeba393/react-native-zendesk.git"
|
|
36
|
+
},
|
|
37
|
+
"bugs": {
|
|
38
|
+
"url": "https://github.com/Haseeba393/react-native-zendesk/issues"
|
|
39
|
+
},
|
|
40
|
+
"homepage": "https://github.com/Haseeba393/react-native-zendesk#readme",
|
|
41
|
+
"engines": {
|
|
42
|
+
"node": ">=18"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"react": "*",
|
|
46
|
+
"react-native": ">=0.72.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/react": "^19.0.12",
|
|
50
|
+
"typescript": "^5.8.2"
|
|
51
|
+
},
|
|
52
|
+
"codegenConfig": {
|
|
53
|
+
"name": "RNZendeskSpec",
|
|
54
|
+
"type": "all",
|
|
55
|
+
"jsSrcsDir": "src",
|
|
56
|
+
"android": {
|
|
57
|
+
"javaPackageName": "com.rnzendesk"
|
|
58
|
+
},
|
|
59
|
+
"ios": {
|
|
60
|
+
"modulesClassName": "RNZendeskSpecProvider"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, "package.json")))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
pod_xcconfig = {
|
|
7
|
+
"CLANG_ENABLE_MODULES" => "YES",
|
|
8
|
+
"CLANG_ENABLE_OBJC_ARC" => "YES",
|
|
9
|
+
"OTHER_CFLAGS" => "$(inherited) -fmodules -fcxx-modules",
|
|
10
|
+
"OTHER_CPLUSPLUSFLAGS" => "$(inherited) -fmodules -fcxx-modules"
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
s.name = "react-native-zendesk"
|
|
14
|
+
s.version = package["version"]
|
|
15
|
+
s.summary = package["description"]
|
|
16
|
+
s.license = package["license"]
|
|
17
|
+
s.authors = package["author"]
|
|
18
|
+
s.homepage = "https://github.com/Haseeba393/react-native-zendesk"
|
|
19
|
+
s.platforms = { :ios => "13.0" }
|
|
20
|
+
s.source = { :git => "https://github.com/Haseeba393/react-native-zendesk.git", :tag => "v#{s.version}" }
|
|
21
|
+
s.source_files = "ios/**/*.{h,m,mm,swift}"
|
|
22
|
+
s.pod_target_xcconfig = pod_xcconfig
|
|
23
|
+
|
|
24
|
+
s.dependency "React-Core"
|
|
25
|
+
s.dependency "React-Codegen"
|
|
26
|
+
s.dependency "ZendeskSupportSDK", "~> 9.0"
|
|
27
|
+
s.dependency "ZendeskCoreSDK", "~> 5.0"
|
|
28
|
+
|
|
29
|
+
if ENV["RCT_NEW_ARCH_ENABLED"] == "1"
|
|
30
|
+
s.compiler_flags = "-DRCT_NEW_ARCH_ENABLED=1"
|
|
31
|
+
s.pod_target_xcconfig = pod_xcconfig.merge({
|
|
32
|
+
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
|
|
33
|
+
"CLANG_CXX_LANGUAGE_STANDARD" => "c++20"
|
|
34
|
+
})
|
|
35
|
+
s.dependency "React-RCTFabric"
|
|
36
|
+
s.dependency "ReactCommon/turbomodule/core"
|
|
37
|
+
end
|
|
38
|
+
end
|