@entrig/react-native 0.0.1
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/EntrigReactNative.podspec +31 -0
- package/LICENSE +21 -0
- package/README.md +609 -0
- package/android/build.gradle +47 -0
- package/android/src/main/AndroidManifest.xml +7 -0
- package/android/src/main/java/com/entrig/reactnative/EntrigModule.kt +190 -0
- package/android/src/main/java/com/entrig/reactnative/EntrigPackage.kt +16 -0
- package/app.plugin.js +1 -0
- package/bin/setup.js +530 -0
- package/build/EntrigModule.d.ts +13 -0
- package/build/EntrigModule.d.ts.map +1 -0
- package/build/EntrigModule.js +38 -0
- package/build/EntrigModule.js.map +1 -0
- package/build/index.d.ts +13 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +48 -0
- package/build/index.js.map +1 -0
- package/build/types.d.ts +16 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +3 -0
- package/build/types.js.map +1 -0
- package/ios/EntrigAppDelegate.swift +58 -0
- package/ios/EntrigModule.m +24 -0
- package/ios/EntrigModule.swift +138 -0
- package/package.json +70 -0
- package/plugin/withEntrig.js +298 -0
- package/react-native.config.js +10 -0
package/bin/setup.js
ADDED
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
|
|
6
|
+
const args = process.argv.slice(2);
|
|
7
|
+
|
|
8
|
+
if (args.length < 1 || args[0] !== "ios") {
|
|
9
|
+
console.log("Usage: npx @entrig/react-native setup ios");
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
console.log("š§ Entrig iOS Setup for React Native\n");
|
|
14
|
+
|
|
15
|
+
// Find iOS directory
|
|
16
|
+
const iosDir = path.join(process.cwd(), "ios");
|
|
17
|
+
if (!fs.existsSync(iosDir)) {
|
|
18
|
+
console.log("ā Error: ios/ directory not found");
|
|
19
|
+
console.log(" Make sure you run this from your React Native project root.");
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Find the app directory in ios/
|
|
24
|
+
let appName = null;
|
|
25
|
+
const iosDirs = fs.readdirSync(iosDir).filter((f) => {
|
|
26
|
+
const stat = fs.statSync(path.join(iosDir, f));
|
|
27
|
+
return (
|
|
28
|
+
stat.isDirectory() &&
|
|
29
|
+
!f.startsWith(".") &&
|
|
30
|
+
f !== "Pods" &&
|
|
31
|
+
f !== "build" &&
|
|
32
|
+
!f.endsWith(".xcodeproj") &&
|
|
33
|
+
!f.endsWith(".xcworkspace")
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
if (iosDirs.length > 0) {
|
|
37
|
+
appName = iosDirs[0];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!appName) {
|
|
41
|
+
console.log("ā Error: Could not determine app name from ios/ directory");
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
console.log(`ā
Found iOS project: ${appName}\n`);
|
|
46
|
+
|
|
47
|
+
// Step 1: Patch AppDelegate
|
|
48
|
+
patchAppDelegate(iosDir, appName);
|
|
49
|
+
|
|
50
|
+
// Step 2: Update entitlements (create if needed)
|
|
51
|
+
updateEntitlements(iosDir, appName);
|
|
52
|
+
|
|
53
|
+
// Step 3: Update Info.plist
|
|
54
|
+
updateInfoPlist(iosDir, appName);
|
|
55
|
+
|
|
56
|
+
console.log("\nš Setup complete! Rebuild your iOS app to apply changes.\n");
|
|
57
|
+
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// AppDelegate patching
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
|
|
62
|
+
function patchAppDelegate(iosDir, appName) {
|
|
63
|
+
// Try Swift first, then ObjC
|
|
64
|
+
const swiftPath = path.join(iosDir, appName, "AppDelegate.swift");
|
|
65
|
+
const objcPath = path.join(iosDir, appName, "AppDelegate.mm");
|
|
66
|
+
const objcMPath = path.join(iosDir, appName, "AppDelegate.m");
|
|
67
|
+
|
|
68
|
+
if (fs.existsSync(swiftPath)) {
|
|
69
|
+
patchSwiftAppDelegate(swiftPath);
|
|
70
|
+
} else if (fs.existsSync(objcPath)) {
|
|
71
|
+
patchObjCAppDelegate(objcPath);
|
|
72
|
+
} else if (fs.existsSync(objcMPath)) {
|
|
73
|
+
patchObjCAppDelegate(objcMPath);
|
|
74
|
+
} else {
|
|
75
|
+
console.log("ā ļø Warning: No AppDelegate found (checked .swift, .mm, .m)");
|
|
76
|
+
console.log(" You will need to manually configure notification handling.");
|
|
77
|
+
console.log(" See ios/EntrigAppDelegate.swift in the package for guidance.\n");
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function patchSwiftAppDelegate(filePath) {
|
|
82
|
+
console.log(`š Checking ${path.relative(process.cwd(), filePath)}...`);
|
|
83
|
+
|
|
84
|
+
let content = fs.readFileSync(filePath, "utf8");
|
|
85
|
+
|
|
86
|
+
// Already configured?
|
|
87
|
+
if (content.includes("Entrig.checkLaunchNotification") || content.includes("EntrigAppDelegate.setup")) {
|
|
88
|
+
console.log("ā
Entrig is already configured in AppDelegate.swift");
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Check for existing delegate methods that would conflict
|
|
93
|
+
if (hasExistingDelegateMethods(content)) {
|
|
94
|
+
printManualSwiftInstructions();
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Backup
|
|
99
|
+
const backupPath = filePath + ".backup";
|
|
100
|
+
fs.copyFileSync(filePath, backupPath);
|
|
101
|
+
console.log(`š¾ Backup created: ${path.relative(process.cwd(), backupPath)}`);
|
|
102
|
+
|
|
103
|
+
// 1. Add imports
|
|
104
|
+
if (!content.includes("import UserNotifications")) {
|
|
105
|
+
// Insert after the first import line
|
|
106
|
+
const importMatch = content.match(/^import \w+/m);
|
|
107
|
+
if (importMatch) {
|
|
108
|
+
content = content.replace(
|
|
109
|
+
importMatch[0],
|
|
110
|
+
importMatch[0] + "\nimport UserNotifications"
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (!content.includes("import EntrigSDK")) {
|
|
116
|
+
const importMatch = content.match(/^import \w+/m);
|
|
117
|
+
if (importMatch) {
|
|
118
|
+
content = content.replace(
|
|
119
|
+
importMatch[0],
|
|
120
|
+
importMatch[0] + "\nimport EntrigSDK"
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 2. Add UNUserNotificationCenterDelegate conformance
|
|
126
|
+
if (!content.includes("UNUserNotificationCenterDelegate")) {
|
|
127
|
+
// Match class declaration patterns:
|
|
128
|
+
// class AppDelegate: RCTAppDelegate {
|
|
129
|
+
// class AppDelegate: UIResponder, UIApplicationDelegate {
|
|
130
|
+
const classPattern = /(class\s+AppDelegate\s*:\s*[^{]+)\{/;
|
|
131
|
+
const classMatch = content.match(classPattern);
|
|
132
|
+
if (classMatch) {
|
|
133
|
+
const declaration = classMatch[1].trimEnd();
|
|
134
|
+
content = content.replace(
|
|
135
|
+
classMatch[0],
|
|
136
|
+
declaration + ", UNUserNotificationCenterDelegate {"
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// 3. Add setup code inside didFinishLaunchingWithOptions
|
|
142
|
+
const didFinishPattern =
|
|
143
|
+
/func application\(\s*_\s+application:\s*UIApplication,\s*didFinishLaunchingWithOptions\s+launchOptions:[^)]*\)\s*->\s*Bool\s*\{/s;
|
|
144
|
+
const didFinishMatch = content.match(didFinishPattern);
|
|
145
|
+
|
|
146
|
+
if (didFinishMatch) {
|
|
147
|
+
const insertPos = didFinishMatch.index + didFinishMatch[0].length;
|
|
148
|
+
const setupCode = `
|
|
149
|
+
// Entrig: Setup push notification handling
|
|
150
|
+
UNUserNotificationCenter.current().delegate = self
|
|
151
|
+
Entrig.checkLaunchNotification(launchOptions)
|
|
152
|
+
`;
|
|
153
|
+
content =
|
|
154
|
+
content.slice(0, insertPos) + setupCode + content.slice(insertPos);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// 4. Add delegate methods before the closing brace of the class
|
|
158
|
+
const delegateMethods = `
|
|
159
|
+
// MARK: - Entrig Push Notification Handling
|
|
160
|
+
|
|
161
|
+
func application(_ application: UIApplication,
|
|
162
|
+
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
|
163
|
+
Entrig.didRegisterForRemoteNotifications(deviceToken: deviceToken)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
func application(_ application: UIApplication,
|
|
167
|
+
didFailToRegisterForRemoteNotificationsWithError error: Error) {
|
|
168
|
+
Entrig.didFailToRegisterForRemoteNotifications(error: error)
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
func userNotificationCenter(_ center: UNUserNotificationCenter,
|
|
172
|
+
willPresent notification: UNNotification,
|
|
173
|
+
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
|
174
|
+
Entrig.willPresentNotification(notification)
|
|
175
|
+
completionHandler(Entrig.getPresentationOptions())
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
func userNotificationCenter(_ center: UNUserNotificationCenter,
|
|
179
|
+
didReceive response: UNNotificationResponse,
|
|
180
|
+
withCompletionHandler completionHandler: @escaping () -> Void) {
|
|
181
|
+
Entrig.didReceiveNotification(response)
|
|
182
|
+
completionHandler()
|
|
183
|
+
}
|
|
184
|
+
`;
|
|
185
|
+
|
|
186
|
+
// Find the last closing brace of the main AppDelegate class
|
|
187
|
+
// We look for the class-level closing brace
|
|
188
|
+
const lastBrace = findClassClosingBrace(content);
|
|
189
|
+
if (lastBrace !== -1) {
|
|
190
|
+
content =
|
|
191
|
+
content.slice(0, lastBrace) + delegateMethods + content.slice(lastBrace);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
fs.writeFileSync(filePath, content);
|
|
195
|
+
|
|
196
|
+
console.log("ā
Successfully configured AppDelegate.swift");
|
|
197
|
+
console.log(" ⢠Added import UserNotifications");
|
|
198
|
+
console.log(" ⢠Added import EntrigSDK");
|
|
199
|
+
console.log(" ⢠Added UNUserNotificationCenterDelegate conformance");
|
|
200
|
+
console.log(" ⢠Added notification delegate setup in didFinishLaunchingWithOptions");
|
|
201
|
+
console.log(" ⢠Added didRegisterForRemoteNotifications");
|
|
202
|
+
console.log(" ⢠Added didFailToRegisterForRemoteNotifications");
|
|
203
|
+
console.log(" ⢠Added userNotificationCenter:willPresent");
|
|
204
|
+
console.log(" ⢠Added userNotificationCenter:didReceive\n");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function patchObjCAppDelegate(filePath) {
|
|
208
|
+
console.log(`š Checking ${path.relative(process.cwd(), filePath)}...`);
|
|
209
|
+
|
|
210
|
+
let content = fs.readFileSync(filePath, "utf8");
|
|
211
|
+
|
|
212
|
+
// Already configured?
|
|
213
|
+
if (content.includes("Entrig") && content.includes("checkLaunchNotification")) {
|
|
214
|
+
console.log("ā
Entrig is already configured in AppDelegate");
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
console.log("ā ļø Objective-C AppDelegate detected.");
|
|
219
|
+
console.log(" Automatic patching is only supported for Swift AppDelegates.");
|
|
220
|
+
console.log(" Please manually add the following to your AppDelegate:\n");
|
|
221
|
+
printManualObjCInstructions();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function hasExistingDelegateMethods(content) {
|
|
225
|
+
const patterns = [
|
|
226
|
+
/func application\([^)]*didRegisterForRemoteNotificationsWithDeviceToken/,
|
|
227
|
+
/func application\([^)]*didFailToRegisterForRemoteNotificationsWithError/,
|
|
228
|
+
/func userNotificationCenter\([^)]*willPresent/,
|
|
229
|
+
/func userNotificationCenter\([^)]*didReceive.*response/,
|
|
230
|
+
];
|
|
231
|
+
|
|
232
|
+
for (const pattern of patterns) {
|
|
233
|
+
if (pattern.test(content)) {
|
|
234
|
+
return true;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
return false;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function findClassClosingBrace(content) {
|
|
241
|
+
// Find "class AppDelegate" then track braces to find its closing }
|
|
242
|
+
const classStart = content.indexOf("class AppDelegate");
|
|
243
|
+
if (classStart === -1) return -1;
|
|
244
|
+
|
|
245
|
+
const openBrace = content.indexOf("{", classStart);
|
|
246
|
+
if (openBrace === -1) return -1;
|
|
247
|
+
|
|
248
|
+
let depth = 1;
|
|
249
|
+
let i = openBrace + 1;
|
|
250
|
+
while (i < content.length && depth > 0) {
|
|
251
|
+
if (content[i] === "{") depth++;
|
|
252
|
+
else if (content[i] === "}") depth--;
|
|
253
|
+
if (depth === 0) return i;
|
|
254
|
+
i++;
|
|
255
|
+
}
|
|
256
|
+
return -1;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function printManualSwiftInstructions() {
|
|
260
|
+
console.log(
|
|
261
|
+
"ā ļø Existing notification delegate methods detected in AppDelegate.swift"
|
|
262
|
+
);
|
|
263
|
+
console.log(
|
|
264
|
+
" Please manually add these calls to your existing methods:\n"
|
|
265
|
+
);
|
|
266
|
+
console.log(" In didFinishLaunchingWithOptions:");
|
|
267
|
+
console.log(" UNUserNotificationCenter.current().delegate = self");
|
|
268
|
+
console.log(" Entrig.checkLaunchNotification(launchOptions)\n");
|
|
269
|
+
console.log(" In didRegisterForRemoteNotificationsWithDeviceToken:");
|
|
270
|
+
console.log(
|
|
271
|
+
" Entrig.didRegisterForRemoteNotifications(deviceToken: deviceToken)\n"
|
|
272
|
+
);
|
|
273
|
+
console.log(" In didFailToRegisterForRemoteNotificationsWithError:");
|
|
274
|
+
console.log(
|
|
275
|
+
" Entrig.didFailToRegisterForRemoteNotifications(error: error)\n"
|
|
276
|
+
);
|
|
277
|
+
console.log(" In userNotificationCenter:willPresent:");
|
|
278
|
+
console.log(" Entrig.willPresentNotification(notification)");
|
|
279
|
+
console.log(
|
|
280
|
+
" completionHandler(Entrig.getPresentationOptions())\n"
|
|
281
|
+
);
|
|
282
|
+
console.log(" In userNotificationCenter:didReceive:");
|
|
283
|
+
console.log(" Entrig.didReceiveNotification(response)\n");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function printManualObjCInstructions() {
|
|
287
|
+
console.log(" @import EntrigSDK;");
|
|
288
|
+
console.log(" #import <UserNotifications/UserNotifications.h>\n");
|
|
289
|
+
console.log(" In didFinishLaunchingWithOptions:");
|
|
290
|
+
console.log(
|
|
291
|
+
" [UNUserNotificationCenter currentNotificationCenter].delegate = self;"
|
|
292
|
+
);
|
|
293
|
+
console.log(
|
|
294
|
+
" [Entrig checkLaunchNotification:launchOptions];\n"
|
|
295
|
+
);
|
|
296
|
+
console.log(" In didRegisterForRemoteNotificationsWithDeviceToken:");
|
|
297
|
+
console.log(
|
|
298
|
+
" [Entrig didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];\n"
|
|
299
|
+
);
|
|
300
|
+
console.log(" In userNotificationCenter:willPresent:");
|
|
301
|
+
console.log(" [Entrig willPresentNotification:notification];");
|
|
302
|
+
console.log(
|
|
303
|
+
" completionHandler([Entrig getPresentationOptions]);\n"
|
|
304
|
+
);
|
|
305
|
+
console.log(" In userNotificationCenter:didReceive:");
|
|
306
|
+
console.log(" [Entrig didReceiveNotification:response];\n");
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// ---------------------------------------------------------------------------
|
|
310
|
+
// Entitlements
|
|
311
|
+
// ---------------------------------------------------------------------------
|
|
312
|
+
|
|
313
|
+
function updateEntitlements(iosDir, appName) {
|
|
314
|
+
// Try common entitlements paths
|
|
315
|
+
const possiblePaths = [
|
|
316
|
+
path.join(iosDir, appName, `${appName}.entitlements`),
|
|
317
|
+
path.join(iosDir, appName, `${appName.replace(/\s/g, "")}.entitlements`),
|
|
318
|
+
];
|
|
319
|
+
|
|
320
|
+
let entitlementsPath = null;
|
|
321
|
+
for (const p of possiblePaths) {
|
|
322
|
+
if (fs.existsSync(p)) {
|
|
323
|
+
entitlementsPath = p;
|
|
324
|
+
break;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// If no entitlements file exists, create one
|
|
329
|
+
if (!entitlementsPath) {
|
|
330
|
+
entitlementsPath = path.join(iosDir, appName, `${appName}.entitlements`);
|
|
331
|
+
const dir = path.dirname(entitlementsPath);
|
|
332
|
+
|
|
333
|
+
if (!fs.existsSync(dir)) {
|
|
334
|
+
console.log(`ā Error: ${dir} not found`);
|
|
335
|
+
return;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const defaultEntitlements = `<?xml version="1.0" encoding="UTF-8"?>
|
|
339
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
340
|
+
<plist version="1.0">
|
|
341
|
+
<dict>
|
|
342
|
+
\t<key>aps-environment</key>
|
|
343
|
+
\t<string>development</string>
|
|
344
|
+
</dict>
|
|
345
|
+
</plist>`;
|
|
346
|
+
|
|
347
|
+
fs.writeFileSync(entitlementsPath, defaultEntitlements);
|
|
348
|
+
console.log(
|
|
349
|
+
`š Created: ${path.relative(process.cwd(), entitlementsPath)}`
|
|
350
|
+
);
|
|
351
|
+
console.log("ā
Added aps-environment to entitlements");
|
|
352
|
+
|
|
353
|
+
// Try to add CODE_SIGN_ENTITLEMENTS to pbxproj
|
|
354
|
+
addEntitlementsToPbxproj(iosDir, appName);
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
console.log(
|
|
359
|
+
`š Checking ${path.relative(process.cwd(), entitlementsPath)}...`
|
|
360
|
+
);
|
|
361
|
+
|
|
362
|
+
let content = fs.readFileSync(entitlementsPath, "utf8");
|
|
363
|
+
|
|
364
|
+
// Check if aps-environment already exists
|
|
365
|
+
if (content.includes("aps-environment")) {
|
|
366
|
+
console.log("ā
aps-environment already configured");
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// Backup
|
|
371
|
+
const backupPath = entitlementsPath + ".backup";
|
|
372
|
+
fs.copyFileSync(entitlementsPath, backupPath);
|
|
373
|
+
console.log(
|
|
374
|
+
`š¾ Backup created: ${path.relative(process.cwd(), backupPath)}`
|
|
375
|
+
);
|
|
376
|
+
|
|
377
|
+
const apsEntry = `\t<key>aps-environment</key>\n\t<string>development</string>\n`;
|
|
378
|
+
|
|
379
|
+
// Handle self-closing <dict/> tag
|
|
380
|
+
if (content.includes("<dict/>")) {
|
|
381
|
+
content = content.replace("<dict/>", `<dict>\n${apsEntry}</dict>`);
|
|
382
|
+
} else {
|
|
383
|
+
// Add aps-environment before closing </dict>
|
|
384
|
+
const insertPoint = content.lastIndexOf("</dict>");
|
|
385
|
+
if (insertPoint === -1) {
|
|
386
|
+
console.log("ā Error: Could not parse entitlements file");
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
content =
|
|
390
|
+
content.slice(0, insertPoint) + apsEntry + content.slice(insertPoint);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
fs.writeFileSync(entitlementsPath, content);
|
|
394
|
+
console.log("ā
Added aps-environment to entitlements");
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Try to add CODE_SIGN_ENTITLEMENTS build setting to the Xcode project
|
|
399
|
+
* so the newly created .entitlements file is picked up automatically.
|
|
400
|
+
*/
|
|
401
|
+
function addEntitlementsToPbxproj(iosDir, appName) {
|
|
402
|
+
const pbxprojPath = path.join(
|
|
403
|
+
iosDir,
|
|
404
|
+
`${appName}.xcodeproj`,
|
|
405
|
+
"project.pbxproj"
|
|
406
|
+
);
|
|
407
|
+
|
|
408
|
+
if (!fs.existsSync(pbxprojPath)) {
|
|
409
|
+
console.log("\nā ļø Could not find project.pbxproj to add entitlements reference.");
|
|
410
|
+
console.log(" You may need to add the entitlements file in Xcode:");
|
|
411
|
+
console.log(` 1. Open ${appName}.xcworkspace in Xcode`);
|
|
412
|
+
console.log(" 2. Select your target ā Signing & Capabilities");
|
|
413
|
+
console.log(" 3. Click + Capability ā Push Notifications\n");
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
let pbxContent = fs.readFileSync(pbxprojPath, "utf8");
|
|
418
|
+
|
|
419
|
+
const entitlementsValue = `${appName}/${appName}.entitlements`;
|
|
420
|
+
|
|
421
|
+
// Already referenced?
|
|
422
|
+
if (pbxContent.includes("CODE_SIGN_ENTITLEMENTS")) {
|
|
423
|
+
console.log("ā
CODE_SIGN_ENTITLEMENTS already in project.pbxproj");
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Backup pbxproj
|
|
428
|
+
const pbxBackup = pbxprojPath + ".backup";
|
|
429
|
+
fs.copyFileSync(pbxprojPath, pbxBackup);
|
|
430
|
+
|
|
431
|
+
// Add CODE_SIGN_ENTITLEMENTS to every XCBuildConfiguration that has a
|
|
432
|
+
// PRODUCT_BUNDLE_IDENTIFIER (i.e. app targets, not Pods/tests)
|
|
433
|
+
const configBlockPattern =
|
|
434
|
+
/(\bPRODUCT_BUNDLE_IDENTIFIER\s*=\s*[^;]+;)/g;
|
|
435
|
+
|
|
436
|
+
let count = 0;
|
|
437
|
+
pbxContent = pbxContent.replace(configBlockPattern, (match) => {
|
|
438
|
+
count++;
|
|
439
|
+
return (
|
|
440
|
+
match +
|
|
441
|
+
`\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = "${entitlementsValue}";`
|
|
442
|
+
);
|
|
443
|
+
});
|
|
444
|
+
|
|
445
|
+
if (count > 0) {
|
|
446
|
+
fs.writeFileSync(pbxprojPath, pbxContent);
|
|
447
|
+
console.log(
|
|
448
|
+
`ā
Added CODE_SIGN_ENTITLEMENTS to project.pbxproj (${count} build configurations)`
|
|
449
|
+
);
|
|
450
|
+
console.log(
|
|
451
|
+
`š¾ Backup created: ${path.relative(process.cwd(), pbxBackup)}`
|
|
452
|
+
);
|
|
453
|
+
} else {
|
|
454
|
+
// Clean up backup if we didn't change anything
|
|
455
|
+
fs.unlinkSync(pbxBackup);
|
|
456
|
+
console.log("\nā ļø Could not auto-add entitlements reference to project.pbxproj.");
|
|
457
|
+
console.log(" Please add Push Notifications capability in Xcode:\n");
|
|
458
|
+
console.log(` 1. Open ${appName}.xcworkspace in Xcode`);
|
|
459
|
+
console.log(" 2. Select your target ā Signing & Capabilities");
|
|
460
|
+
console.log(" 3. Click + Capability ā Push Notifications\n");
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// ---------------------------------------------------------------------------
|
|
465
|
+
// Info.plist
|
|
466
|
+
// ---------------------------------------------------------------------------
|
|
467
|
+
|
|
468
|
+
function updateInfoPlist(iosDir, appName) {
|
|
469
|
+
const infoPlistPath = path.join(iosDir, appName, "Info.plist");
|
|
470
|
+
|
|
471
|
+
if (!fs.existsSync(infoPlistPath)) {
|
|
472
|
+
console.log(
|
|
473
|
+
`ā ļø Warning: ${path.relative(process.cwd(), infoPlistPath)} not found`
|
|
474
|
+
);
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
console.log(
|
|
479
|
+
`š Checking ${path.relative(process.cwd(), infoPlistPath)}...`
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
let content = fs.readFileSync(infoPlistPath, "utf8");
|
|
483
|
+
|
|
484
|
+
// Check if UIBackgroundModes with remote-notification already exists
|
|
485
|
+
if (
|
|
486
|
+
content.includes("UIBackgroundModes") &&
|
|
487
|
+
content.includes("remote-notification")
|
|
488
|
+
) {
|
|
489
|
+
console.log("ā
UIBackgroundModes already configured");
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
// Backup
|
|
494
|
+
const backupPath = infoPlistPath + ".backup";
|
|
495
|
+
fs.copyFileSync(infoPlistPath, backupPath);
|
|
496
|
+
console.log(
|
|
497
|
+
`š¾ Backup created: ${path.relative(process.cwd(), backupPath)}`
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
if (content.includes("UIBackgroundModes")) {
|
|
501
|
+
// Add remote-notification to existing array
|
|
502
|
+
const arrayMatch = content.match(
|
|
503
|
+
/<key>UIBackgroundModes<\/key>\s*<array>/
|
|
504
|
+
);
|
|
505
|
+
if (arrayMatch) {
|
|
506
|
+
const insertPoint = arrayMatch.index + arrayMatch[0].length;
|
|
507
|
+
const newEntry = "\n\t\t<string>remote-notification</string>";
|
|
508
|
+
content =
|
|
509
|
+
content.slice(0, insertPoint) +
|
|
510
|
+
newEntry +
|
|
511
|
+
content.slice(insertPoint);
|
|
512
|
+
}
|
|
513
|
+
} else {
|
|
514
|
+
// Add UIBackgroundModes array before closing </dict>
|
|
515
|
+
const plistEnd = content.lastIndexOf("</plist>");
|
|
516
|
+
const dictEnd = content.lastIndexOf("</dict>", plistEnd);
|
|
517
|
+
|
|
518
|
+
if (dictEnd === -1) {
|
|
519
|
+
console.log("ā Error: Could not parse Info.plist");
|
|
520
|
+
return;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
const bgModes = `\t<key>UIBackgroundModes</key>\n\t<array>\n\t\t<string>remote-notification</string>\n\t</array>\n`;
|
|
524
|
+
content =
|
|
525
|
+
content.slice(0, dictEnd) + bgModes + content.slice(dictEnd);
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
fs.writeFileSync(infoPlistPath, content);
|
|
529
|
+
console.log("ā
Added remote-notification to UIBackgroundModes");
|
|
530
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { EntrigConfig, NotificationEvent } from './types';
|
|
2
|
+
declare class EntrigModuleClass {
|
|
3
|
+
init(config: EntrigConfig): Promise<void>;
|
|
4
|
+
register(userId: string, isDebug?: boolean): Promise<void>;
|
|
5
|
+
requestPermission(): Promise<boolean>;
|
|
6
|
+
unregister(): Promise<void>;
|
|
7
|
+
getInitialNotification(): Promise<NotificationEvent | null>;
|
|
8
|
+
onForegroundNotification(callback: (event: NotificationEvent) => void): import("react-native").EmitterSubscription;
|
|
9
|
+
onNotificationOpened(callback: (event: NotificationEvent) => void): import("react-native").EmitterSubscription;
|
|
10
|
+
}
|
|
11
|
+
declare const _default: EntrigModuleClass;
|
|
12
|
+
export default _default;
|
|
13
|
+
//# sourceMappingURL=EntrigModule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntrigModule.d.ts","sourceRoot":"","sources":["../src/EntrigModule.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAY1D,cAAM,iBAAiB;IACf,IAAI,CAAC,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAIzC,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAI1D,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC;IAIrC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAI3B,sBAAsB,IAAI,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IAKjE,wBAAwB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI;IAKrE,oBAAoB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI;CAIlE;;AAED,wBAAuC"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const react_native_1 = require("react-native");
|
|
4
|
+
const RNEntrigModule = react_native_1.NativeModules.Entrig;
|
|
5
|
+
if (!RNEntrigModule) {
|
|
6
|
+
throw new Error('Native Entrig module not found. Make sure you have linked the library correctly.');
|
|
7
|
+
}
|
|
8
|
+
// Create event emitter for native events
|
|
9
|
+
const eventEmitter = new react_native_1.NativeEventEmitter(RNEntrigModule);
|
|
10
|
+
// Export the native module with proper type annotations
|
|
11
|
+
class EntrigModuleClass {
|
|
12
|
+
async init(config) {
|
|
13
|
+
return RNEntrigModule.initialize(config);
|
|
14
|
+
}
|
|
15
|
+
async register(userId, isDebug) {
|
|
16
|
+
return RNEntrigModule.register(userId, isDebug ?? null);
|
|
17
|
+
}
|
|
18
|
+
async requestPermission() {
|
|
19
|
+
return RNEntrigModule.requestPermission();
|
|
20
|
+
}
|
|
21
|
+
async unregister() {
|
|
22
|
+
return RNEntrigModule.unregister();
|
|
23
|
+
}
|
|
24
|
+
async getInitialNotification() {
|
|
25
|
+
return RNEntrigModule.getInitialNotification();
|
|
26
|
+
}
|
|
27
|
+
// Event listener methods
|
|
28
|
+
onForegroundNotification(callback) {
|
|
29
|
+
const subscription = eventEmitter.addListener('onForegroundNotification', callback);
|
|
30
|
+
return subscription;
|
|
31
|
+
}
|
|
32
|
+
onNotificationOpened(callback) {
|
|
33
|
+
const subscription = eventEmitter.addListener('onNotificationOpened', callback);
|
|
34
|
+
return subscription;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
exports.default = new EntrigModuleClass();
|
|
38
|
+
//# sourceMappingURL=EntrigModule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EntrigModule.js","sourceRoot":"","sources":["../src/EntrigModule.ts"],"names":[],"mappings":";;AAAA,+CAAiE;AAIjE,MAAM,cAAc,GAAG,4BAAa,CAAC,MAAM,CAAC;AAE5C,IAAI,CAAC,cAAc,EAAE,CAAC;IACpB,MAAM,IAAI,KAAK,CAAC,kFAAkF,CAAC,CAAC;AACtG,CAAC;AAED,yCAAyC;AACzC,MAAM,YAAY,GAAG,IAAI,iCAAkB,CAAC,cAAc,CAAC,CAAC;AAE5D,wDAAwD;AACxD,MAAM,iBAAiB;IACrB,KAAK,CAAC,IAAI,CAAC,MAAoB;QAC7B,OAAO,cAAc,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,OAAiB;QAC9C,OAAO,cAAc,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,IAAI,IAAI,CAAC,CAAC;IAC1D,CAAC;IAED,KAAK,CAAC,iBAAiB;QACrB,OAAO,cAAc,CAAC,iBAAiB,EAAE,CAAC;IAC5C,CAAC;IAED,KAAK,CAAC,UAAU;QACd,OAAO,cAAc,CAAC,UAAU,EAAE,CAAC;IACrC,CAAC;IAED,KAAK,CAAC,sBAAsB;QAC1B,OAAO,cAAc,CAAC,sBAAsB,EAAE,CAAC;IACjD,CAAC;IAED,yBAAyB;IACzB,wBAAwB,CAAC,QAA4C;QACnE,MAAM,YAAY,GAAG,YAAY,CAAC,WAAW,CAAC,0BAA0B,EAAE,QAAQ,CAAC,CAAC;QACpF,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,oBAAoB,CAAC,QAA4C;QAC/D,MAAM,YAAY,GAAG,YAAY,CAAC,WAAW,CAAC,sBAAsB,EAAE,QAAQ,CAAC,CAAC;QAChF,OAAO,YAAY,CAAC;IACtB,CAAC;CACF;AAED,kBAAe,IAAI,iBAAiB,EAAE,CAAC"}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { NotificationEvent } from './types';
|
|
2
|
+
export { default } from './EntrigModule';
|
|
3
|
+
export * from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Hook to listen for Entrig notification events.
|
|
6
|
+
* The callback is stored in a ref so consumers don't need to memoize it ā
|
|
7
|
+
* the listener subscribes once and stays stable across re-renders.
|
|
8
|
+
*
|
|
9
|
+
* @param eventType - 'foreground' or 'opened'
|
|
10
|
+
* @param callback - Function to call when event fires
|
|
11
|
+
*/
|
|
12
|
+
export declare function useEntrigEvent(eventType: 'foreground' | 'opened', callback: (event: NotificationEvent) => void): void;
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AACzC,cAAc,SAAS,CAAC;AAExB;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,SAAS,EAAE,YAAY,GAAG,QAAQ,EAClC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,QAiB7C"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.default = void 0;
|
|
21
|
+
exports.useEntrigEvent = useEntrigEvent;
|
|
22
|
+
const react_1 = require("react");
|
|
23
|
+
const EntrigModule_1 = __importDefault(require("./EntrigModule"));
|
|
24
|
+
var EntrigModule_2 = require("./EntrigModule");
|
|
25
|
+
Object.defineProperty(exports, "default", { enumerable: true, get: function () { return __importDefault(EntrigModule_2).default; } });
|
|
26
|
+
__exportStar(require("./types"), exports);
|
|
27
|
+
/**
|
|
28
|
+
* Hook to listen for Entrig notification events.
|
|
29
|
+
* The callback is stored in a ref so consumers don't need to memoize it ā
|
|
30
|
+
* the listener subscribes once and stays stable across re-renders.
|
|
31
|
+
*
|
|
32
|
+
* @param eventType - 'foreground' or 'opened'
|
|
33
|
+
* @param callback - Function to call when event fires
|
|
34
|
+
*/
|
|
35
|
+
function useEntrigEvent(eventType, callback) {
|
|
36
|
+
const callbackRef = (0, react_1.useRef)(callback);
|
|
37
|
+
callbackRef.current = callback;
|
|
38
|
+
(0, react_1.useEffect)(() => {
|
|
39
|
+
const handler = (event) => callbackRef.current(event);
|
|
40
|
+
const subscription = eventType === 'foreground'
|
|
41
|
+
? EntrigModule_1.default.onForegroundNotification(handler)
|
|
42
|
+
: EntrigModule_1.default.onNotificationOpened(handler);
|
|
43
|
+
return () => {
|
|
44
|
+
subscription.remove();
|
|
45
|
+
};
|
|
46
|
+
}, [eventType]);
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAeA,wCAmBC;AAlCD,iCAA0C;AAE1C,kEAA0C;AAE1C,+CAAyC;AAAhC,wHAAA,OAAO,OAAA;AAChB,0CAAwB;AAExB;;;;;;;GAOG;AACH,SAAgB,cAAc,CAC5B,SAAkC,EAClC,QAA4C;IAE5C,MAAM,WAAW,GAAG,IAAA,cAAM,EAAC,QAAQ,CAAC,CAAC;IACrC,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;IAE/B,IAAA,iBAAS,EAAC,GAAG,EAAE;QACb,MAAM,OAAO,GAAG,CAAC,KAAwB,EAAE,EAAE,CAAC,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAEzE,MAAM,YAAY,GAChB,SAAS,KAAK,YAAY;YACxB,CAAC,CAAC,sBAAY,CAAC,wBAAwB,CAAC,OAAO,CAAC;YAChD,CAAC,CAAC,sBAAY,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;QAEjD,OAAO,GAAG,EAAE;YACV,YAAY,CAAC,MAAM,EAAE,CAAC;QACxB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;AAClB,CAAC"}
|
package/build/types.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export type NotificationEvent = {
|
|
2
|
+
title: string;
|
|
3
|
+
body: string;
|
|
4
|
+
data: Record<string, any>;
|
|
5
|
+
isForeground: boolean;
|
|
6
|
+
};
|
|
7
|
+
export type EntrigConfig = {
|
|
8
|
+
apiKey: string;
|
|
9
|
+
handlePermission?: boolean;
|
|
10
|
+
showForegroundNotification?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export type EntrigModuleEvents = {
|
|
13
|
+
onForegroundNotification: (event: NotificationEvent) => void;
|
|
14
|
+
onNotificationOpened: (event: NotificationEvent) => void;
|
|
15
|
+
};
|
|
16
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC1B,YAAY,EAAE,OAAO,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,0BAA0B,CAAC,EAAE,OAAO,CAAC;CACtC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,wBAAwB,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;IAC7D,oBAAoB,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAC;CAC1D,CAAC"}
|
package/build/types.js
ADDED