capacitor-messenger-notifications 1.0.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.
Files changed (32) hide show
  1. package/LICENSE +21 -0
  2. package/MessengerNotifications.podspec +18 -0
  3. package/Package.swift +26 -0
  4. package/README.md +152 -0
  5. package/android/build.gradle +62 -0
  6. package/android/src/main/AndroidManifest.xml +19 -0
  7. package/android/src/main/java/com/codecraft_studio/messenger/notifications/EncryptedMessageNotifier.java +68 -0
  8. package/android/src/main/java/com/codecraft_studio/messenger/notifications/FcmFetchManager.java +37 -0
  9. package/android/src/main/java/com/codecraft_studio/messenger/notifications/MessengerNotificationsPlugin.java +92 -0
  10. package/android/src/main/java/com/codecraft_studio/messenger/notifications/NativeCrypto.java +43 -0
  11. package/android/src/main/java/com/codecraft_studio/messenger/notifications/NotificationDismissReceiver.java +20 -0
  12. package/android/src/main/java/com/codecraft_studio/messenger/notifications/NotificationHelper.java +618 -0
  13. package/android/src/main/java/com/codecraft_studio/messenger/notifications/PersistentSocketService.java +213 -0
  14. package/dist/esm/definitions.d.ts +48 -0
  15. package/dist/esm/definitions.js +2 -0
  16. package/dist/esm/definitions.js.map +1 -0
  17. package/dist/esm/index.d.ts +4 -0
  18. package/dist/esm/index.js +7 -0
  19. package/dist/esm/index.js.map +1 -0
  20. package/dist/esm/web.d.ts +25 -0
  21. package/dist/esm/web.js +42 -0
  22. package/dist/esm/web.js.map +1 -0
  23. package/dist/plugin.cjs.js +56 -0
  24. package/dist/plugin.cjs.js.map +1 -0
  25. package/dist/plugin.js +59 -0
  26. package/dist/plugin.js.map +1 -0
  27. package/ios/Sources/MessengerNotificationsPlugin/MessengerNotificationsPlugin.swift +76 -0
  28. package/ios/Sources/MessengerNotificationsPlugin/NativeCrypto.swift +22 -0
  29. package/ios/Sources/MessengerNotificationsPlugin/NotificationHelper.swift +58 -0
  30. package/ios/Sources/MessengerNotificationsPlugin/SafeStorageStore.swift +28 -0
  31. package/ios/Sources/MessengerNotificationsPlugin/TemporarySocketSessionManager.swift +186 -0
  32. package/package.json +77 -0
@@ -0,0 +1,28 @@
1
+ import Foundation
2
+
3
+ public enum SafeStorageStore {
4
+ private static let rootKey = "messenger_plugin_storage"
5
+
6
+ public static func getAll() -> [String: String] {
7
+ guard let dict = UserDefaults.standard.dictionary(forKey: rootKey) as? [String: String] else {
8
+ return [:]
9
+ }
10
+ return dict
11
+ }
12
+
13
+ public static func get(_ key: String) -> String? {
14
+ return getAll()[key]
15
+ }
16
+
17
+ public static func set(_ key: String, value: String?) {
18
+ var dict = getAll()
19
+ dict[key] = value
20
+ UserDefaults.standard.set(dict, forKey: rootKey)
21
+ }
22
+
23
+ public static func remove(_ key: String) {
24
+ var dict = getAll()
25
+ dict.removeValue(forKey: key)
26
+ UserDefaults.standard.set(dict, forKey: rootKey)
27
+ }
28
+ }
@@ -0,0 +1,186 @@
1
+ import Foundation
2
+ import SocketIO
3
+ import os.log
4
+
5
+ public enum TemporarySocketSessionManager {
6
+ private static let log = OSLog(subsystem: Bundle.main.bundleIdentifier ?? "com.codecraft_studio.messenger.notifications",
7
+ category: "Notifications")
8
+ private static let defaultIdleTimeout: TimeInterval = 15
9
+ private static let defaultConnectTimeout: TimeInterval = 20
10
+ private static let defaultMaxSession: TimeInterval = 45
11
+
12
+ private static let messageEvents: Set<String> = [
13
+ "sync_messages_response",
14
+ "sync:messages",
15
+ "room:message_notification"
16
+ ]
17
+
18
+ public static func runSession(payloadData: [String: Any]?, completion: @escaping (Bool) -> Void) {
19
+ let jwt = SafeStorageStore.get("auth_token") ?? ""
20
+ let socketFromPayload = payloadData?["socketUrl"] as? String
21
+ let socketFromPrefs = SafeStorageStore.get("socket_url")
22
+ let base = socketFromPayload ?? socketFromPrefs ?? "wss://4.rw"
23
+
24
+ if jwt.isEmpty {
25
+ completion(false)
26
+ return
27
+ }
28
+
29
+ let url = normalizeSocketBaseUrl(base)
30
+ guard let socketURL = URL(string: url) else {
31
+ completion(false)
32
+ return
33
+ }
34
+
35
+ let manager = SocketManager(
36
+ socketURL: socketURL,
37
+ config: [
38
+ .forceNew(true),
39
+ .reconnects(false),
40
+ .log(false),
41
+ .connectParams([
42
+ "token": jwt,
43
+ "auth[token]": jwt
44
+ ]),
45
+ .compress
46
+ ]
47
+ )
48
+
49
+ let socket = manager.defaultSocket
50
+ var finished = false
51
+ var messageReceived = false
52
+ var syncResponseReceived = false
53
+
54
+ let finish: () -> Void = {
55
+ if finished { return }
56
+ finished = true
57
+ socket.removeAllHandlers()
58
+ socket.disconnect()
59
+ completion(messageReceived)
60
+ }
61
+
62
+ var idleTimer: Timer?
63
+ func resetIdleTimer() {
64
+ idleTimer?.invalidate()
65
+ idleTimer = Timer.scheduledTimer(withTimeInterval: defaultIdleTimeout, repeats: false) { _ in
66
+ finish()
67
+ }
68
+ }
69
+
70
+ socket.on(clientEvent: .connect) { _, _ in
71
+ resetIdleTimer()
72
+ if let payloadData = payloadData {
73
+ let roomId = (payloadData["roomId"] as? Int) ?? (payloadData["room_id"] as? Int) ?? 0
74
+ if roomId > 0 {
75
+ socket.emit("join_room", String(roomId))
76
+ }
77
+ }
78
+ socket.emit("sync_messages")
79
+ }
80
+
81
+ socket.onAny { event in
82
+ let name = event.event
83
+ let items = event.items ?? []
84
+
85
+ if name == "sync_messages_response" {
86
+ syncResponseReceived = true
87
+ }
88
+
89
+ if messageEvents.contains(name) {
90
+ if handleSocketArgs(event: name, args: items, syncReceived: syncResponseReceived) {
91
+ messageReceived = true
92
+ resetIdleTimer()
93
+ }
94
+ }
95
+ }
96
+
97
+ socket.on(clientEvent: .disconnect) { _, _ in
98
+ finish()
99
+ }
100
+
101
+ socket.connect(timeoutAfter: defaultConnectTimeout) {
102
+ finish()
103
+ }
104
+
105
+ DispatchQueue.main.asyncAfter(deadline: .now() + defaultMaxSession) {
106
+ if !finished { finish() }
107
+ }
108
+ }
109
+
110
+ private static func normalizeSocketBaseUrl(_ baseUrl: String) -> String {
111
+ var normalized = baseUrl.trimmingCharacters(in: .whitespacesAndNewlines)
112
+ if normalized.hasSuffix("/") { normalized = String(normalized.dropLast()) }
113
+ if normalized.hasSuffix("/api") { normalized = String(normalized.dropLast(4)) }
114
+ if !normalized.contains("://") { normalized = "https://\(normalized)" }
115
+ return normalized
116
+ }
117
+
118
+ private static func handleSocketArgs(event: String, args: [Any], syncReceived: Bool) -> Bool {
119
+ guard !args.isEmpty else { return false }
120
+ var handled = false
121
+ for arg in args {
122
+ guard let dict = arg as? [String: Any] ?? (arg as? NSDictionary as? [String: Any]) else {
123
+ continue
124
+ }
125
+ if handleNormalizedRecord(dict, isSync: event == "sync_messages_response") {
126
+ handled = true
127
+ }
128
+ }
129
+ return handled
130
+ }
131
+
132
+ private static func handleNormalizedRecord(_ payload: [String: Any], isSync: Bool) -> Bool {
133
+ guard let roomId = (payload["room_id"] as? Int) ?? (payload["roomId"] as? Int),
134
+ roomId > 0 else { return false }
135
+
136
+ let senderId = (payload["sender_id"] as? Int) ?? (payload["senderId"] as? Int) ?? 0
137
+
138
+ let encryptedMessage = payload["encrypted_message"] as? String
139
+ ?? payload["encryptedMessage"] as? String
140
+ ?? payload["ciphertext"] as? String
141
+
142
+ let encryptedUsername = payload["encrypted_username"] as? String
143
+ ?? payload["encryptedUsername"] as? String
144
+
145
+ let encryptedRoomName = payload["encrypted_room_name"] as? String
146
+ ?? payload["encryptedRoomName"] as? String
147
+
148
+ var roomName: String?
149
+ if let encRoom = encryptedRoomName {
150
+ roomName = try? NativeCrypto.decryptRoomData(roomId: roomId, encryptedJSON: encRoom).text
151
+ }
152
+
153
+ var username: String?
154
+ if let encUser = encryptedUsername {
155
+ username = try? NativeCrypto.decryptUserData(userId: senderId, encryptedJSON: encUser).text
156
+ }
157
+
158
+ var messageText: String?
159
+ if let encMsg = encryptedMessage {
160
+ messageText = try? NativeCrypto.decryptRoomData(roomId: roomId, encryptedJSON: encMsg).text
161
+ }
162
+
163
+ let finalTitle = username ?? roomName ?? "New message"
164
+ let finalBody = messageText ?? "New encrypted message"
165
+
166
+ let messageId = (payload["id"] as? String)
167
+ ?? (payload["messageId"] as? String)
168
+ ?? (payload["message_id"] as? String)
169
+
170
+ let timestampMs: Int64 = {
171
+ if let ts = payload["timestamp"] as? Int64 { return ts }
172
+ if let ts = payload["timestamp"] as? Int { return Int64(ts) }
173
+ return Int64(Date().timeIntervalSince1970 * 1000)
174
+ }()
175
+
176
+ NotificationHelper.showRoomNotification(
177
+ title: finalTitle,
178
+ body: finalBody,
179
+ roomId: roomId,
180
+ messageId: messageId,
181
+ timestamp: timestampMs,
182
+ isSync: isSync
183
+ )
184
+ return true
185
+ }
186
+ }
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "capacitor-messenger-notifications",
3
+ "version": "1.0.0",
4
+ "description": "Capacitor plugin for managing messenger notifications using a web socket in Android and iOS.",
5
+ "main": "dist/plugin.cjs.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/esm/index.d.ts",
8
+ "unpkg": "dist/plugin.js",
9
+ "files": [
10
+ "android/src/main/",
11
+ "android/build.gradle",
12
+ "dist/",
13
+ "ios/Sources",
14
+ "Package.swift",
15
+ "MessengerNotifications.podspec"
16
+ ],
17
+ "author": "Muhammad Nadeem",
18
+ "license": "MIT",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/imuhammadnadeem/capacitor-messenger-notifications.git"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/imuhammadnadeem/capacitor-messenger-notifications/issues"
25
+ },
26
+ "keywords": [
27
+ "capacitor",
28
+ "capacitor-plugin",
29
+ "messenger",
30
+ "notifications",
31
+ "socket.io",
32
+ "android",
33
+ "ios"
34
+ ],
35
+ "scripts": {
36
+ "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
37
+ "verify:ios": "xcodebuild -scheme MessengerNotifications -destination 'generic/platform=iOS' -configuration Debug build",
38
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
39
+ "verify:web": "npm run build",
40
+ "lint": "npm run eslint && npm run prettier",
41
+ "fmt": "npm run eslint -- --fix && npm run prettier -- --write",
42
+ "eslint": "eslint . --ext ts",
43
+ "prettier": "prettier \"{src,android/src}/**/*.{css,html,ts,js,java}\" --write --plugin=prettier-plugin-java",
44
+ "build": "npm run clean && tsc && rollup -c rollup.config.mjs",
45
+ "clean": "rimraf ./dist",
46
+ "watch": "tsc --watch",
47
+ "prepublishOnly": "npm run build"
48
+ },
49
+ "devDependencies": {
50
+ "@capacitor/android": "^6.0.0",
51
+ "@capacitor/core": "^6.0.0",
52
+ "@capacitor/ios": "^6.0.0",
53
+ "@ionic/eslint-config": "^0.4.0",
54
+ "@ionic/prettier-config": "^4.0.0",
55
+ "eslint": "^8.57.1",
56
+ "prettier": "^3.6.2",
57
+ "prettier-plugin-java": "^2.7.7",
58
+ "rimraf": "^6.1.0",
59
+ "rollup": "^4.53.2",
60
+ "typescript": "^5.5.0"
61
+ },
62
+ "peerDependencies": {
63
+ "@capacitor/core": "^6.0.0 || ^7.0.0 || ^8.0.0"
64
+ },
65
+ "prettier": "@ionic/prettier-config",
66
+ "eslintConfig": {
67
+ "extends": "@ionic/eslint-config/recommended"
68
+ },
69
+ "capacitor": {
70
+ "ios": {
71
+ "src": "ios"
72
+ },
73
+ "android": {
74
+ "src": "android"
75
+ }
76
+ }
77
+ }