@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import UIKit
|
|
2
|
+
import UserNotifications
|
|
3
|
+
import EntrigSDK
|
|
4
|
+
|
|
5
|
+
/// Helper class that wires up the iOS AppDelegate hooks required by the Entrig SDK.
|
|
6
|
+
///
|
|
7
|
+
/// **For Expo users**: The config plugin (`app.plugin.js`) injects these hooks
|
|
8
|
+
/// at prebuild time, so this file is not used.
|
|
9
|
+
///
|
|
10
|
+
/// **For bare React Native users**: Call `EntrigAppDelegate.setup(launchOptions:)`
|
|
11
|
+
/// from your AppDelegate's `didFinishLaunchingWithOptions` and forward the
|
|
12
|
+
/// other delegate methods as shown below.
|
|
13
|
+
///
|
|
14
|
+
/// ```swift
|
|
15
|
+
/// // AppDelegate.swift
|
|
16
|
+
/// import EntrigSDK
|
|
17
|
+
///
|
|
18
|
+
/// @main
|
|
19
|
+
/// class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
|
|
20
|
+
/// func application(_ application: UIApplication,
|
|
21
|
+
/// didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
|
22
|
+
/// UNUserNotificationCenter.current().delegate = self
|
|
23
|
+
/// EntrigAppDelegate.setup(launchOptions: launchOptions)
|
|
24
|
+
/// return true
|
|
25
|
+
/// }
|
|
26
|
+
///
|
|
27
|
+
/// func application(_ application: UIApplication,
|
|
28
|
+
/// didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
|
29
|
+
/// Entrig.didRegisterForRemoteNotifications(deviceToken: deviceToken)
|
|
30
|
+
/// }
|
|
31
|
+
///
|
|
32
|
+
/// func application(_ application: UIApplication,
|
|
33
|
+
/// didFailToRegisterForRemoteNotificationsWithError error: Error) {
|
|
34
|
+
/// Entrig.didFailToRegisterForRemoteNotifications(error: error)
|
|
35
|
+
/// }
|
|
36
|
+
///
|
|
37
|
+
/// func userNotificationCenter(_ center: UNUserNotificationCenter,
|
|
38
|
+
/// willPresent notification: UNNotification,
|
|
39
|
+
/// withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
|
40
|
+
/// Entrig.willPresentNotification(notification)
|
|
41
|
+
/// completionHandler(Entrig.getPresentationOptions())
|
|
42
|
+
/// }
|
|
43
|
+
///
|
|
44
|
+
/// func userNotificationCenter(_ center: UNUserNotificationCenter,
|
|
45
|
+
/// didReceive response: UNNotificationResponse,
|
|
46
|
+
/// withCompletionHandler completionHandler: @escaping () -> Void) {
|
|
47
|
+
/// Entrig.didReceiveNotification(response)
|
|
48
|
+
/// completionHandler()
|
|
49
|
+
/// }
|
|
50
|
+
/// }
|
|
51
|
+
/// ```
|
|
52
|
+
@objc public class EntrigAppDelegate: NSObject {
|
|
53
|
+
|
|
54
|
+
/// Call from `didFinishLaunchingWithOptions` to check for cold-start notifications.
|
|
55
|
+
@objc public static func setup(launchOptions: [UIApplication.LaunchOptionsKey: Any]?) {
|
|
56
|
+
Entrig.checkLaunchNotification(launchOptions)
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#import <React/RCTBridgeModule.h>
|
|
2
|
+
#import <React/RCTEventEmitter.h>
|
|
3
|
+
|
|
4
|
+
@interface RCT_EXTERN_MODULE(Entrig, RCTEventEmitter)
|
|
5
|
+
|
|
6
|
+
RCT_EXTERN_METHOD(initialize:(NSDictionary *)config
|
|
7
|
+
withResolver:(RCTPromiseResolveBlock)resolve
|
|
8
|
+
withRejecter:(RCTPromiseRejectBlock)reject)
|
|
9
|
+
|
|
10
|
+
RCT_EXTERN_METHOD(register:(NSString *)userId
|
|
11
|
+
withIsDebug:(NSNumber *)isDebug
|
|
12
|
+
withResolver:(RCTPromiseResolveBlock)resolve
|
|
13
|
+
withRejecter:(RCTPromiseRejectBlock)reject)
|
|
14
|
+
|
|
15
|
+
RCT_EXTERN_METHOD(requestPermission:(RCTPromiseResolveBlock)resolve
|
|
16
|
+
withRejecter:(RCTPromiseRejectBlock)reject)
|
|
17
|
+
|
|
18
|
+
RCT_EXTERN_METHOD(unregister:(RCTPromiseResolveBlock)resolve
|
|
19
|
+
withRejecter:(RCTPromiseRejectBlock)reject)
|
|
20
|
+
|
|
21
|
+
RCT_EXTERN_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve
|
|
22
|
+
withRejecter:(RCTPromiseRejectBlock)reject)
|
|
23
|
+
|
|
24
|
+
@end
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import React
|
|
2
|
+
import UserNotifications
|
|
3
|
+
import EntrigSDK
|
|
4
|
+
|
|
5
|
+
@objc(Entrig)
|
|
6
|
+
class EntrigModule: RCTEventEmitter {
|
|
7
|
+
private var hasListeners = false
|
|
8
|
+
|
|
9
|
+
override func supportedEvents() -> [String]! {
|
|
10
|
+
return ["onForegroundNotification", "onNotificationOpened"]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
override class func requiresMainQueueSetup() -> Bool {
|
|
14
|
+
return true
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
override func startObserving() {
|
|
18
|
+
hasListeners = true
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
override func stopObserving() {
|
|
22
|
+
hasListeners = false
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@objc(initialize:withResolver:withRejecter:)
|
|
26
|
+
func initializeSDK(config: [String: Any], resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
|
27
|
+
guard let apiKey = config["apiKey"] as? String, !apiKey.isEmpty else {
|
|
28
|
+
rejecter("INVALID_API_KEY", "API key is required and cannot be empty", nil)
|
|
29
|
+
return
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
let handlePermission = config["handlePermission"] as? Bool ?? true
|
|
33
|
+
let showForegroundNotification = config["showForegroundNotification"] as? Bool ?? true
|
|
34
|
+
let entrigConfig = EntrigConfig(apiKey: apiKey, handlePermission: handlePermission, showForegroundNotification: showForegroundNotification)
|
|
35
|
+
|
|
36
|
+
// Set up SDK listeners
|
|
37
|
+
Entrig.setOnForegroundNotificationListener(self)
|
|
38
|
+
Entrig.setOnNotificationOpenedListener(self)
|
|
39
|
+
|
|
40
|
+
Entrig.configure(config: entrigConfig) { success, error in
|
|
41
|
+
if success {
|
|
42
|
+
resolver(nil)
|
|
43
|
+
} else {
|
|
44
|
+
rejecter("INIT_ERROR", error ?? "Failed to initialize SDK", nil)
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
@objc(register:withIsDebug:withResolver:withRejecter:)
|
|
50
|
+
func register(userId: String, isDebugOverride: NSNumber?, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
|
51
|
+
let isDebug: Bool
|
|
52
|
+
if let override = isDebugOverride {
|
|
53
|
+
isDebug = override.boolValue
|
|
54
|
+
} else {
|
|
55
|
+
#if DEBUG
|
|
56
|
+
isDebug = true
|
|
57
|
+
#else
|
|
58
|
+
isDebug = false
|
|
59
|
+
#endif
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
Entrig.register(userId: userId, sdk: "react-native", isDebug: isDebug) { success, error in
|
|
63
|
+
if success {
|
|
64
|
+
resolver(nil)
|
|
65
|
+
} else {
|
|
66
|
+
rejecter("REGISTER_ERROR", error ?? "Registration failed", nil)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@objc(requestPermission:withRejecter:)
|
|
72
|
+
func requestPermission(resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
|
73
|
+
Entrig.requestPermission { granted, error in
|
|
74
|
+
if let error = error {
|
|
75
|
+
rejecter("PERMISSION_ERROR", error.localizedDescription, nil)
|
|
76
|
+
} else {
|
|
77
|
+
resolver(granted)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
@objc(unregister:withRejecter:)
|
|
83
|
+
func unregister(resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
|
84
|
+
Entrig.unregister { success, error in
|
|
85
|
+
if success {
|
|
86
|
+
resolver(nil)
|
|
87
|
+
} else {
|
|
88
|
+
rejecter("UNREGISTER_ERROR", error ?? "Unregistration failed", nil)
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
@objc(getInitialNotification:withRejecter:)
|
|
94
|
+
func getInitialNotification(resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) {
|
|
95
|
+
if let event = Entrig.getInitialNotification() {
|
|
96
|
+
let payload: [String: Any] = [
|
|
97
|
+
"title": event.title ?? "",
|
|
98
|
+
"body": event.body ?? "",
|
|
99
|
+
"data": event.data ?? [:],
|
|
100
|
+
"isForeground": false
|
|
101
|
+
]
|
|
102
|
+
resolver(payload)
|
|
103
|
+
} else {
|
|
104
|
+
resolver(nil)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Helper method to send notification events to JavaScript
|
|
109
|
+
private func sendNotificationToJS(event: NotificationEvent, isForeground: Bool) {
|
|
110
|
+
guard hasListeners else { return }
|
|
111
|
+
|
|
112
|
+
let payload: [String: Any] = [
|
|
113
|
+
"title": event.title ?? "",
|
|
114
|
+
"body": event.body ?? "",
|
|
115
|
+
"data": event.data ?? [:],
|
|
116
|
+
"isForeground": isForeground
|
|
117
|
+
]
|
|
118
|
+
|
|
119
|
+
if isForeground {
|
|
120
|
+
sendEvent(withName: "onForegroundNotification", body: payload)
|
|
121
|
+
} else {
|
|
122
|
+
sendEvent(withName: "onNotificationOpened", body: payload)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// MARK: - SDK Listeners
|
|
128
|
+
extension EntrigModule: OnNotificationReceivedListener {
|
|
129
|
+
func onNotificationReceived(_ event: NotificationEvent) {
|
|
130
|
+
sendNotificationToJS(event: event, isForeground: true)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
extension EntrigModule: OnNotificationClickListener {
|
|
135
|
+
func onNotificationClick(_ event: NotificationEvent) {
|
|
136
|
+
sendNotificationToJS(event: event, isForeground: false)
|
|
137
|
+
}
|
|
138
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@entrig/react-native",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Entrig | Push Notifications for Supabase",
|
|
5
|
+
"main": "build/index.js",
|
|
6
|
+
"types": "build/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"entrig-react-native": "./bin/setup.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"build/",
|
|
12
|
+
"ios/",
|
|
13
|
+
"android/",
|
|
14
|
+
"plugin/",
|
|
15
|
+
"bin/",
|
|
16
|
+
"app.plugin.js",
|
|
17
|
+
"react-native.config.js",
|
|
18
|
+
"EntrigReactNative.podspec"
|
|
19
|
+
],
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc",
|
|
25
|
+
"clean": "rm -rf build/",
|
|
26
|
+
"lint": "eslint src/ --ext .ts,.tsx",
|
|
27
|
+
"test": "jest",
|
|
28
|
+
"prepare": "npm run build"
|
|
29
|
+
},
|
|
30
|
+
"keywords": [
|
|
31
|
+
"react-native",
|
|
32
|
+
"expo",
|
|
33
|
+
"entrig",
|
|
34
|
+
"push-notifications",
|
|
35
|
+
"supabase",
|
|
36
|
+
"notifications",
|
|
37
|
+
"apns",
|
|
38
|
+
"fcm"
|
|
39
|
+
],
|
|
40
|
+
"repository": "https://github.com/entrig/entrig-react-native",
|
|
41
|
+
"bugs": {
|
|
42
|
+
"url": "https://github.com/entrig/entrig-react-native/issues"
|
|
43
|
+
},
|
|
44
|
+
"author": "entrig <team@entrig.com> (https://github.com/entrig)",
|
|
45
|
+
"license": "MIT",
|
|
46
|
+
"homepage": "https://github.com/entrig/entrig-react-native#readme",
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@expo/config-plugins": "^54.0.4",
|
|
49
|
+
"@types/react": "^19.0.0",
|
|
50
|
+
"typescript": "^5.0.0"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"@expo/config-plugins": ">=7.0.0",
|
|
54
|
+
"react": "*",
|
|
55
|
+
"react-native": "*"
|
|
56
|
+
},
|
|
57
|
+
"peerDependenciesMeta": {
|
|
58
|
+
"@expo/config-plugins": {
|
|
59
|
+
"optional": true
|
|
60
|
+
},
|
|
61
|
+
"expo": {
|
|
62
|
+
"optional": true
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
"expo": {
|
|
66
|
+
"autolinking": {
|
|
67
|
+
"nativeModulesDir": "."
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
const {
|
|
2
|
+
withInfoPlist,
|
|
3
|
+
withEntitlementsPlist,
|
|
4
|
+
withDangerousMod,
|
|
5
|
+
} = require('@expo/config-plugins');
|
|
6
|
+
const fs = require('fs');
|
|
7
|
+
const path = require('path');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Expo config plugin for Entrig React Native SDK
|
|
11
|
+
* Automatically configures push notification entitlements, capabilities,
|
|
12
|
+
* and AppDelegate hooks.
|
|
13
|
+
*
|
|
14
|
+
* Add to app.json:
|
|
15
|
+
* {
|
|
16
|
+
* "expo": {
|
|
17
|
+
* "plugins": ["@entrig/react-native"]
|
|
18
|
+
* }
|
|
19
|
+
* }
|
|
20
|
+
*/
|
|
21
|
+
function withEntrig(config) {
|
|
22
|
+
config = withInfoPlist(config, modifyInfoPlist);
|
|
23
|
+
config = withEntitlementsPlist(config, modifyEntitlementsPlist);
|
|
24
|
+
config = withDangerousMod(config, ['ios', modifyAppDelegate]);
|
|
25
|
+
return config;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Add UIBackgroundModes with remote-notification to Info.plist
|
|
30
|
+
*/
|
|
31
|
+
function modifyInfoPlist(config) {
|
|
32
|
+
const infoPlist = config.modResults;
|
|
33
|
+
|
|
34
|
+
if (!infoPlist.UIBackgroundModes) {
|
|
35
|
+
infoPlist.UIBackgroundModes = [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!infoPlist.UIBackgroundModes.includes('remote-notification')) {
|
|
39
|
+
infoPlist.UIBackgroundModes.push('remote-notification');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return config;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Add APS environment to entitlements
|
|
47
|
+
*/
|
|
48
|
+
function modifyEntitlementsPlist(config) {
|
|
49
|
+
const entitlements = config.modResults;
|
|
50
|
+
|
|
51
|
+
if (!entitlements['aps-environment']) {
|
|
52
|
+
entitlements['aps-environment'] = 'development';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return config;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Inject AppDelegate hooks for push notification handling.
|
|
60
|
+
* Handles both Swift (modern Expo/RN 0.71+) and ObjC AppDelegates.
|
|
61
|
+
*/
|
|
62
|
+
async function modifyAppDelegate(config) {
|
|
63
|
+
const iosRoot = path.join(config.modRequest.platformProjectRoot);
|
|
64
|
+
|
|
65
|
+
// Find the app delegate file
|
|
66
|
+
const appName = config.modRequest.projectName || config.name;
|
|
67
|
+
const swiftPath = path.join(iosRoot, appName, 'AppDelegate.swift');
|
|
68
|
+
const objcPath = path.join(iosRoot, appName, 'AppDelegate.mm');
|
|
69
|
+
|
|
70
|
+
if (fs.existsSync(swiftPath)) {
|
|
71
|
+
patchSwiftAppDelegate(swiftPath);
|
|
72
|
+
} else if (fs.existsSync(objcPath)) {
|
|
73
|
+
patchObjCAppDelegate(objcPath);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return config;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Swift AppDelegate patching (Expo prebuild generates Swift by default)
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
|
|
83
|
+
function patchSwiftAppDelegate(filePath) {
|
|
84
|
+
let content = fs.readFileSync(filePath, 'utf8');
|
|
85
|
+
|
|
86
|
+
// Already configured?
|
|
87
|
+
if (content.includes('Entrig.checkLaunchNotification') || content.includes('EntrigSDK')) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// 1. Add imports
|
|
92
|
+
if (!content.includes('import UserNotifications')) {
|
|
93
|
+
const importMatch = content.match(/^import \w+/m);
|
|
94
|
+
if (importMatch) {
|
|
95
|
+
content = content.replace(
|
|
96
|
+
importMatch[0],
|
|
97
|
+
importMatch[0] + '\nimport UserNotifications'
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (!content.includes('import EntrigSDK')) {
|
|
103
|
+
const importMatch = content.match(/^import \w+/m);
|
|
104
|
+
if (importMatch) {
|
|
105
|
+
content = content.replace(
|
|
106
|
+
importMatch[0],
|
|
107
|
+
importMatch[0] + '\nimport EntrigSDK'
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 2. Add UNUserNotificationCenterDelegate conformance to the class
|
|
113
|
+
if (!content.includes('UNUserNotificationCenterDelegate')) {
|
|
114
|
+
const classPattern = /(class\s+AppDelegate\s*:\s*[^{]+)\{/;
|
|
115
|
+
const classMatch = content.match(classPattern);
|
|
116
|
+
if (classMatch) {
|
|
117
|
+
const declaration = classMatch[1].trimEnd();
|
|
118
|
+
content = content.replace(
|
|
119
|
+
classMatch[0],
|
|
120
|
+
declaration + ', UNUserNotificationCenterDelegate {'
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// 3. Add setup code inside didFinishLaunchingWithOptions
|
|
126
|
+
if (!content.includes('Entrig.checkLaunchNotification')) {
|
|
127
|
+
const didFinishPattern =
|
|
128
|
+
/func application\(\s*_\s+application:\s*UIApplication,\s*didFinishLaunchingWithOptions\s+launchOptions:[^)]*\)\s*->\s*Bool\s*\{/s;
|
|
129
|
+
const didFinishMatch = content.match(didFinishPattern);
|
|
130
|
+
|
|
131
|
+
if (didFinishMatch) {
|
|
132
|
+
const insertPos = didFinishMatch.index + didFinishMatch[0].length;
|
|
133
|
+
const setupCode = `
|
|
134
|
+
// Entrig: Setup push notification handling
|
|
135
|
+
UNUserNotificationCenter.current().delegate = self
|
|
136
|
+
Entrig.checkLaunchNotification(launchOptions)
|
|
137
|
+
`;
|
|
138
|
+
content = content.slice(0, insertPos) + setupCode + content.slice(insertPos);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// 4. Add delegate methods before the closing brace of the AppDelegate class
|
|
143
|
+
if (!content.includes('didRegisterForRemoteNotificationsWithDeviceToken')) {
|
|
144
|
+
const delegateMethods = `
|
|
145
|
+
// MARK: - Entrig Push Notification Handling
|
|
146
|
+
|
|
147
|
+
override func application(_ application: UIApplication,
|
|
148
|
+
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
|
149
|
+
Entrig.didRegisterForRemoteNotifications(deviceToken: deviceToken)
|
|
150
|
+
super.application(application, didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
override func application(_ application: UIApplication,
|
|
154
|
+
didFailToRegisterForRemoteNotificationsWithError error: Error) {
|
|
155
|
+
Entrig.didFailToRegisterForRemoteNotifications(error: error)
|
|
156
|
+
super.application(application, didFailToRegisterForRemoteNotificationsWithError: error)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
func userNotificationCenter(_ center: UNUserNotificationCenter,
|
|
160
|
+
willPresent notification: UNNotification,
|
|
161
|
+
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
|
|
162
|
+
Entrig.willPresentNotification(notification)
|
|
163
|
+
completionHandler(Entrig.getPresentationOptions())
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
func userNotificationCenter(_ center: UNUserNotificationCenter,
|
|
167
|
+
didReceive response: UNNotificationResponse,
|
|
168
|
+
withCompletionHandler completionHandler: @escaping () -> Void) {
|
|
169
|
+
Entrig.didReceiveNotification(response)
|
|
170
|
+
completionHandler()
|
|
171
|
+
}
|
|
172
|
+
`;
|
|
173
|
+
|
|
174
|
+
const lastBrace = findClassClosingBrace(content);
|
|
175
|
+
if (lastBrace !== -1) {
|
|
176
|
+
content = content.slice(0, lastBrace) + delegateMethods + content.slice(lastBrace);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
fs.writeFileSync(filePath, content);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
// ObjC AppDelegate patching (older Expo/RN projects)
|
|
185
|
+
// ---------------------------------------------------------------------------
|
|
186
|
+
|
|
187
|
+
function patchObjCAppDelegate(filePath) {
|
|
188
|
+
let content = fs.readFileSync(filePath, 'utf8');
|
|
189
|
+
|
|
190
|
+
// Already configured?
|
|
191
|
+
if (content.includes('[Entrig checkLaunchNotification') || content.includes('EntrigSDK')) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// 1. Add imports
|
|
196
|
+
if (!content.includes('UserNotifications/UserNotifications.h')) {
|
|
197
|
+
content = '#import <UserNotifications/UserNotifications.h>\n' + content;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
if (!content.includes('@import EntrigSDK')) {
|
|
201
|
+
content = '@import EntrigSDK;\n' + content;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// 2. Add UNUserNotificationCenterDelegate to interface
|
|
205
|
+
if (!content.includes('UNUserNotificationCenterDelegate')) {
|
|
206
|
+
const interfacePattern = /(@interface\s+AppDelegate[^>]*>)/;
|
|
207
|
+
const interfaceMatch = content.match(interfacePattern);
|
|
208
|
+
if (interfaceMatch) {
|
|
209
|
+
// Check if there's already a protocol list
|
|
210
|
+
if (interfaceMatch[0].includes('<')) {
|
|
211
|
+
// Add to existing protocol list before the closing >
|
|
212
|
+
content = content.replace(
|
|
213
|
+
interfaceMatch[0],
|
|
214
|
+
interfaceMatch[0].replace('>', ', UNUserNotificationCenterDelegate>')
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// 3. Add setup in didFinishLaunchingWithOptions
|
|
221
|
+
if (!content.includes('[Entrig checkLaunchNotification')) {
|
|
222
|
+
const didFinishPattern =
|
|
223
|
+
/(-\s*\(BOOL\)application:.*?didFinishLaunchingWithOptions:.*?\{)/s;
|
|
224
|
+
const didFinishMatch = content.match(didFinishPattern);
|
|
225
|
+
|
|
226
|
+
if (didFinishMatch) {
|
|
227
|
+
const insertPos = didFinishMatch.index + didFinishMatch[0].length;
|
|
228
|
+
const setupCode = `
|
|
229
|
+
// Entrig: Setup push notification handling
|
|
230
|
+
[UNUserNotificationCenter currentNotificationCenter].delegate = self;
|
|
231
|
+
[Entrig checkLaunchNotification:launchOptions];
|
|
232
|
+
`;
|
|
233
|
+
content = content.slice(0, insertPos) + setupCode + content.slice(insertPos);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// 4. Add delegate methods before @end
|
|
238
|
+
if (!content.includes('didRegisterForRemoteNotificationsWithDeviceToken')) {
|
|
239
|
+
const endIdx = content.lastIndexOf('@end');
|
|
240
|
+
if (endIdx !== -1) {
|
|
241
|
+
const delegateMethods = `
|
|
242
|
+
// MARK: - Entrig Push Notification Handling
|
|
243
|
+
|
|
244
|
+
- (void)application:(UIApplication *)application
|
|
245
|
+
didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
|
|
246
|
+
[Entrig didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
- (void)application:(UIApplication *)application
|
|
250
|
+
didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
|
|
251
|
+
[Entrig didFailToRegisterForRemoteNotificationsWithError:error];
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
|
|
255
|
+
willPresentNotification:(UNNotification *)notification
|
|
256
|
+
withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler {
|
|
257
|
+
[Entrig willPresentNotification:notification];
|
|
258
|
+
completionHandler([Entrig getPresentationOptions]);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
|
|
262
|
+
didReceiveNotificationResponse:(UNNotificationResponse *)response
|
|
263
|
+
withCompletionHandler:(void (^)(void))completionHandler {
|
|
264
|
+
[Entrig didReceiveNotification:response];
|
|
265
|
+
completionHandler();
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
`;
|
|
269
|
+
content = content.slice(0, endIdx) + delegateMethods + content.slice(endIdx);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
fs.writeFileSync(filePath, content);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// ---------------------------------------------------------------------------
|
|
277
|
+
// Helpers
|
|
278
|
+
// ---------------------------------------------------------------------------
|
|
279
|
+
|
|
280
|
+
function findClassClosingBrace(content) {
|
|
281
|
+
const classStart = content.indexOf('class AppDelegate');
|
|
282
|
+
if (classStart === -1) return -1;
|
|
283
|
+
|
|
284
|
+
const openBrace = content.indexOf('{', classStart);
|
|
285
|
+
if (openBrace === -1) return -1;
|
|
286
|
+
|
|
287
|
+
let depth = 1;
|
|
288
|
+
let i = openBrace + 1;
|
|
289
|
+
while (i < content.length && depth > 0) {
|
|
290
|
+
if (content[i] === '{') depth++;
|
|
291
|
+
else if (content[i] === '}') depth--;
|
|
292
|
+
if (depth === 0) return i;
|
|
293
|
+
i++;
|
|
294
|
+
}
|
|
295
|
+
return -1;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
module.exports = withEntrig;
|