diff --git a/example/exampleApp/android/app/src/main/java/com/exampleapp/MainApplication.kt b/example/exampleApp/android/app/src/main/java/com/exampleapp/MainApplication.kt index fa43b14..0afa558 100644 --- a/example/exampleApp/android/app/src/main/java/com/exampleapp/MainApplication.kt +++ b/example/exampleApp/android/app/src/main/java/com/exampleapp/MainApplication.kt @@ -1,16 +1,11 @@ package com.exampleapp import android.app.Application -import android.content.Context -import android.content.SharedPreferences -import android.os.Handler -import android.os.Looper -import android.util.Log +import cloud.mindbox.mobile_sdk.Mindbox import cloud.mindbox.mobile_sdk.pushes.MindboxRemoteMessage import cloud.mindbox.mindbox_firebase.MindboxFirebase import cloud.mindbox.mindbox_huawei.MindboxHuawei -import cloud.mindbox.mobile_sdk.Mindbox -import com.exampleapp.NotificationPackage +import cloud.mindbox.mindbox_rustore.MindboxRuStore import com.facebook.react.PackageList import com.facebook.react.ReactApplication import com.facebook.react.ReactHost @@ -18,12 +13,8 @@ import com.facebook.react.ReactPackage import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost import com.facebook.react.defaults.DefaultReactNativeHost -import com.facebook.react.modules.core.DeviceEventManagerModule import com.facebook.react.soloader.OpenSourceMergedSoMapping import com.facebook.soloader.SoLoader -import com.google.gson.Gson -import com.google.gson.reflect.TypeToken -import cloud.mindbox.mindbox_rustore.MindboxRuStore class MainApplication : Application(), ReactApplication { @@ -55,31 +46,8 @@ class MainApplication : Application(), ReactApplication { } } - private val gson = Gson() - fun saveNotification(message: MindboxRemoteMessage) { - val sharedPreferences = getSharedPreferences("notifications", Context.MODE_PRIVATE) - val editor = sharedPreferences.edit() - val notificationsJson = sharedPreferences.getString("notifications", "[]") - val type = object : TypeToken>() {}.type - val notifications: MutableList = gson.fromJson(notificationsJson, type) - notifications.add(gson.toJson(message)) - editor.putString("notifications", gson.toJson(notifications)) - editor.apply() - notifyJS() - } - - private fun notifyJS() { - val handler = Handler(Looper.getMainLooper()) - handler.post { - try { - val reactInstanceManager = reactNativeHost.reactInstanceManager - reactInstanceManager.currentReactContext - ?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) - ?.emit("newNotification", null) - } catch (e: Exception) { - Log.e("MainApplication", "Error notifying React Native", e) - } - } + NotificationStorage.saveNotification(this, message) + NotificationModule.emitNotificationCenterUpdatedFromExternal() } } diff --git a/example/exampleApp/android/app/src/main/java/com/exampleapp/NotificationModule.kt b/example/exampleApp/android/app/src/main/java/com/exampleapp/NotificationModule.kt index 10bc93c..a64a3e4 100644 --- a/example/exampleApp/android/app/src/main/java/com/exampleapp/NotificationModule.kt +++ b/example/exampleApp/android/app/src/main/java/com/exampleapp/NotificationModule.kt @@ -1,49 +1,57 @@ package com.exampleapp -import android.content.Context -import android.content.SharedPreferences +import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.ReactMethod +import com.facebook.react.module.annotations.ReactModule -class NotificationModule(reactContext: ReactApplicationContext) : - ReactContextBaseJavaModule(reactContext) { +@ReactModule(name = NativeNotificationModuleSpec.NAME) +class NotificationModule( + private val reactContext: ReactApplicationContext +) : NativeNotificationModuleSpec(reactContext) { - private val sharedPreferences: SharedPreferences = - reactContext.getSharedPreferences("notifications", Context.MODE_PRIVATE) + companion object { + @Volatile + private var activeModule: NotificationModule? = null - override fun getName(): String { - return "NotificationModule" - } - - @ReactMethod - fun addListener(eventName: String?) { + fun emitNotificationCenterUpdatedFromExternal() { + activeModule?.emitNotificationCenterUpdated() + } } - @ReactMethod - fun removeListeners(count: Integer?) { + init { + activeModule = this } @ReactMethod - fun getNotifications(promise: Promise) { + override fun getNotifications(promise: Promise) { try { - val notificationsJson = sharedPreferences.getString("notifications", "[]") + val notificationsJson: String = NotificationStorage.getNotificationsJson(reactContext) promise.resolve(notificationsJson) - } catch (e: Exception) { - promise.reject("Error", e) + } catch (error: Throwable) { + promise.reject("Error", error) } } @ReactMethod - fun clearNotifications(promise: Promise) { + override fun clearNotifications(promise: Promise) { try { - val editor = sharedPreferences.edit() - editor.putString("notifications", "[]") - editor.apply() + NotificationStorage.clearNotifications(reactContext) promise.resolve(null) - } catch (e: Exception) { - promise.reject("Error", e) + } catch (error: Throwable) { + promise.reject("Error", error) } } + + override fun invalidate() { + if (activeModule === this) { + activeModule = null + } + super.invalidate() + } + + private fun emitNotificationCenterUpdated() { + emitOnNotificationCenterUpdated(Arguments.createMap()) + } } diff --git a/example/exampleApp/android/app/src/main/java/com/exampleapp/NotificationPackage.kt b/example/exampleApp/android/app/src/main/java/com/exampleapp/NotificationPackage.kt index 020a3a8..ce7aa2a 100644 --- a/example/exampleApp/android/app/src/main/java/com/exampleapp/NotificationPackage.kt +++ b/example/exampleApp/android/app/src/main/java/com/exampleapp/NotificationPackage.kt @@ -1,17 +1,35 @@ package com.exampleapp -import com.facebook.react.ReactPackage +import com.facebook.react.TurboReactPackage import com.facebook.react.bridge.NativeModule import com.facebook.react.bridge.ReactApplicationContext -import com.facebook.react.uimanager.ViewManager +import com.facebook.react.module.model.ReactModuleInfo +import com.facebook.react.module.model.ReactModuleInfoProvider +import com.facebook.react.turbomodule.core.interfaces.TurboModule -class NotificationPackage : ReactPackage { +class NotificationPackage : TurboReactPackage() { - override fun createViewManagers(reactContext: ReactApplicationContext): List> { - return emptyList() + override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? { + return if (name == NativeNotificationModuleSpec.NAME) { + NotificationModule(reactContext) + } else { + null + } } - override fun createNativeModules(reactContext: ReactApplicationContext): List { - return listOf(NotificationModule(reactContext)) + override fun getReactModuleInfoProvider(): ReactModuleInfoProvider { + return ReactModuleInfoProvider { + mapOf( + NativeNotificationModuleSpec.NAME to ReactModuleInfo( + NativeNotificationModuleSpec.NAME, + NativeNotificationModuleSpec.NAME, + false, + false, + false, + false, + TurboModule::class.java.isAssignableFrom(NotificationModule::class.java) + ) + ) + } } } diff --git a/example/exampleApp/android/app/src/main/java/com/exampleapp/NotificationStorage.kt b/example/exampleApp/android/app/src/main/java/com/exampleapp/NotificationStorage.kt new file mode 100644 index 0000000..776bf72 --- /dev/null +++ b/example/exampleApp/android/app/src/main/java/com/exampleapp/NotificationStorage.kt @@ -0,0 +1,47 @@ +package com.exampleapp + +import android.content.Context +import cloud.mindbox.mobile_sdk.pushes.MindboxRemoteMessage +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import java.lang.reflect.Type + +object NotificationStorage { + private const val PREFERENCES_NAME: String = "notifications" + private const val NOTIFICATIONS_KEY: String = "notifications" + private const val EMPTY_NOTIFICATIONS_JSON: String = "[]" + private val gson: Gson = Gson() + private val notificationListType: Type = object : TypeToken>() {}.type + + @Synchronized + fun saveNotification(context: Context, message: MindboxRemoteMessage) { + val notifications: MutableList = readNotifications(context).toMutableList() + notifications.add(gson.toJson(message)) + context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE) + .edit() + .putString(NOTIFICATIONS_KEY, gson.toJson(notifications)) + .apply() + } + + @Synchronized + fun getNotificationsJson(context: Context): String { + return context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE) + .getString(NOTIFICATIONS_KEY, EMPTY_NOTIFICATIONS_JSON) + ?: EMPTY_NOTIFICATIONS_JSON + } + + @Synchronized + fun clearNotifications(context: Context) { + context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE) + .edit() + .putString(NOTIFICATIONS_KEY, EMPTY_NOTIFICATIONS_JSON) + .apply() + } + + private fun readNotifications(context: Context): List { + val notificationsJson: String = getNotificationsJson(context) + return runCatching { + gson.fromJson>(notificationsJson, notificationListType) + }.getOrNull() ?: emptyList() + } +} diff --git a/example/exampleApp/ios/AppDelegate.swift b/example/exampleApp/ios/AppDelegate.swift index 20d7e43..8b73fd0 100644 --- a/example/exampleApp/ios/AppDelegate.swift +++ b/example/exampleApp/ios/AppDelegate.swift @@ -31,10 +31,8 @@ class AppDelegate: RCTAppDelegate, UNUserNotificationCenterDelegate { return super.application(application, didFinishLaunchingWithOptions: launchOptions) } - func notifyReactNative() { - if let eventEmitter = bridge?.module(for: NotificationModule.self) as? NotificationModule { - eventEmitter.notifyReactNative() - } + func notifyReactNativeAboutNotificationCenterUpdate() { + NotificationCenter.default.post(name: NotificationCenterStorage.notificationCenterUpdatedName, object: nil) } // Handling remote notification fetch completion @@ -42,7 +40,7 @@ class AppDelegate: RCTAppDelegate, UNUserNotificationCenterDelegate { didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { Mindbox.shared.application(application, performFetchWithCompletionHandler: completionHandler) - notifyReactNative() + notifyReactNativeAboutNotificationCenterUpdate() } // Updating APNS token in Mindbox @@ -63,7 +61,7 @@ class AppDelegate: RCTAppDelegate, UNUserNotificationCenterDelegate { // Displaying notifications when the app is active func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { - notifyReactNative() + notifyReactNativeAboutNotificationCenterUpdate() completionHandler([.alert, .sound, .badge]) } @@ -80,11 +78,6 @@ class AppDelegate: RCTAppDelegate, UNUserNotificationCenterDelegate { override func sourceURL(for bridge: RCTBridge!) -> URL! { bundleURL() } - - override func extraModules(for bridge: RCTBridge!) -> [RCTBridgeModule] { - [NotificationModule()] - } - override func bundleURL() -> URL? { #if DEBUG RCTBundleURLProvider.sharedSettings().jsBundleURL(forBundleRoot: "index") diff --git a/example/exampleApp/ios/MindboxNotificationServiceExtension/NotificationService.swift b/example/exampleApp/ios/MindboxNotificationServiceExtension/NotificationService.swift index 50aff5b..7c0dad8 100644 --- a/example/exampleApp/ios/MindboxNotificationServiceExtension/NotificationService.swift +++ b/example/exampleApp/ios/MindboxNotificationServiceExtension/NotificationService.swift @@ -4,7 +4,7 @@ import MindboxNotifications // https://developers.mindbox.ru/docs/ios-send-rich-push-react-native class NotificationService: UNNotificationServiceExtension { - static let suiteName = "group.cloud.Mindbox.com.mindbox.exampleRN" + static let suiteName = "group.cloud.Mindbox.mindbox.RN.Example" // Lazy initialization of MindboxNotificationService lazy var mindboxService = MindboxNotificationService() diff --git a/example/exampleApp/ios/NotificationCenterStorage.swift b/example/exampleApp/ios/NotificationCenterStorage.swift new file mode 100644 index 0000000..d1dde96 --- /dev/null +++ b/example/exampleApp/ios/NotificationCenterStorage.swift @@ -0,0 +1,28 @@ +import Foundation + +@objc(NotificationCenterStorage) +final class NotificationCenterStorage: NSObject { + static let suiteName: String = "group.cloud.Mindbox.mindbox.RN.Example" + static let notificationCenterUpdatedRawName: String = "NotificationCenterUpdated" + static let notificationCenterUpdatedName: Notification.Name = Notification.Name(notificationCenterUpdatedRawName) + private static let notificationsKey: String = "notifications" + private static let emptyNotificationsJson: String = "[]" + + @objc static func getNotificationCenterUpdatedName() -> String { + return notificationCenterUpdatedRawName + } + + @objc static func getNotificationsJson() -> String { + guard let userDefaults: UserDefaults = UserDefaults(suiteName: suiteName) else { + return emptyNotificationsJson + } + return userDefaults.string(forKey: notificationsKey) ?? emptyNotificationsJson + } + + @objc static func clearNotifications() { + let userDefaults: UserDefaults? = UserDefaults(suiteName: suiteName) + userDefaults?.set(emptyNotificationsJson, forKey: notificationsKey) + userDefaults?.synchronize() + } + +} diff --git a/example/exampleApp/ios/NotificationModule.m b/example/exampleApp/ios/NotificationModule.m deleted file mode 100644 index 6194b78..0000000 --- a/example/exampleApp/ios/NotificationModule.m +++ /dev/null @@ -1,7 +0,0 @@ -#import -#import - -@interface RCT_EXTERN_MODULE(NotificationModule, RCTEventEmitter) -RCT_EXTERN_METHOD(getNotifications:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(clearNotifications:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) -@end diff --git a/example/exampleApp/ios/NotificationModule.mm b/example/exampleApp/ios/NotificationModule.mm new file mode 100644 index 0000000..dd188a5 --- /dev/null +++ b/example/exampleApp/ios/NotificationModule.mm @@ -0,0 +1,64 @@ +#import +#import + +#if __has_include() +#import +#elif __has_include("ExampleAppSpec.h") +#import "ExampleAppSpec.h" +#else +#error "ExampleAppSpec.h not found. Ensure React Native codegen is enabled for exampleApp." +#endif + +@interface NotificationCenterStorage : NSObject ++ (NSString *)getNotificationCenterUpdatedName; ++ (NSString *)getNotificationsJson; ++ (void)clearNotifications; +@end + +@interface NotificationModule : NativeNotificationModuleSpecBase +@end + +@implementation NotificationModule + +RCT_EXPORT_MODULE(NotificationModule) + ++ (BOOL)requiresMainQueueSetup { + return YES; +} + +- (instancetype)init { + self = [super init]; + if (self) { + NSString *eventName = [NotificationCenterStorage getNotificationCenterUpdatedName]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotificationCenterUpdated:) name:eventName object:nil]; + } + return self; +} + +- (void)dealloc { + [[NSNotificationCenter defaultCenter] removeObserver:self]; +} + +- (void)getNotifications:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject { + resolve([NotificationCenterStorage getNotificationsJson]); +} + +- (void)clearNotifications:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject { + [NotificationCenterStorage clearNotifications]; + resolve(nil); +} + +- (void)handleNotificationCenterUpdated:(NSNotification *)notification { + if (!_eventEmitterCallback) { + return; + } + [self emitOnNotificationCenterUpdated:@{}]; +} + +- (std::shared_ptr)getTurboModule:(const facebook::react::ObjCTurboModule::InitParams &)params { + return std::make_shared(params); +} + +@end diff --git a/example/exampleApp/ios/NotificationModule.swift b/example/exampleApp/ios/NotificationModule.swift deleted file mode 100644 index 421d3af..0000000 --- a/example/exampleApp/ios/NotificationModule.swift +++ /dev/null @@ -1,47 +0,0 @@ -import Foundation -import React - -@objc(NotificationModule) -class NotificationModule: RCTEventEmitter { - - static let suiteName = "group.cloud.Mindbox.com.mindbox.exampleRN" - private var hasListeners = false - - override static func requiresMainQueueSetup() -> Bool { - return true - } - - override func supportedEvents() -> [String]! { - return ["newNotification"] - } - - // send notification data to RN - @objc func getNotifications(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) { - let userDefaults = UserDefaults(suiteName: "group.cloud.Mindbox.com.mindbox.exampleRN") - let notificationsJson = userDefaults?.string(forKey: "notifications") ?? "[]" - resolve(notificationsJson) - } - - //clear notifications data on native part - @objc func clearNotifications(_ resolve: @escaping RCTPromiseResolveBlock, rejecter reject: @escaping RCTPromiseRejectBlock) { - let userDefaults = UserDefaults(suiteName: NotificationModule.suiteName) - userDefaults?.removeObject(forKey: "notifications") - userDefaults?.synchronize() - resolve(nil) - } - - override func startObserving() { - hasListeners = true - } - - override func stopObserving() { - hasListeners = false - } - - // send event about new notification - func notifyReactNative() { - if hasListeners { - sendEvent(withName: "newNotification", body: nil) - } - } -} diff --git a/example/exampleApp/ios/exampleApp.xcodeproj/project.pbxproj b/example/exampleApp/ios/exampleApp.xcodeproj/project.pbxproj index babf5b9..273adf1 100644 --- a/example/exampleApp/ios/exampleApp.xcodeproj/project.pbxproj +++ b/example/exampleApp/ios/exampleApp.xcodeproj/project.pbxproj @@ -9,8 +9,8 @@ /* Begin PBXBuildFile section */ 02ADDBF8A19984F9D26FCB4E /* libPods-exampleApp-MindboxNotificationServiceExtension.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 9D15FE27D8E0EFFC720594B8 /* libPods-exampleApp-MindboxNotificationServiceExtension.a */; }; 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; - 3A027BAC2C3BF24F005415BB /* NotificationModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A027BAB2C3BF24F005415BB /* NotificationModule.swift */; }; - 3A027BAE2C3BFC4D005415BB /* NotificationModule.m in Sources */ = {isa = PBXBuildFile; fileRef = 3A027BAD2C3BFC4D005415BB /* NotificationModule.m */; }; + 3A027BAC2C3BF24F005415BB /* NotificationCenterStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A027BAB2C3BF24F005415BB /* NotificationCenterStorage.swift */; }; + 3A027BAE2C3BFC4D005415BB /* NotificationModule.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3A027BAD2C3BFC4D005415BB /* NotificationModule.mm */; }; 3A175C362C3D1B800027776A /* PushAction.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A175C322C3D16C70027776A /* PushAction.swift */; }; 3A175C372C3D1B800027776A /* MindboxRemoteMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A175C342C3D16F50027776A /* MindboxRemoteMessage.swift */; }; 3A88FC092B6BDF360046E687 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A88FC082B6BDF360046E687 /* AppDelegate.swift */; }; @@ -64,8 +64,8 @@ 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = exampleApp/Info.plist; sourceTree = ""; }; 22407D2A15E8083A8FEC02A6 /* Pods-exampleApp-MindboxNotificationServiceExtension.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-exampleApp-MindboxNotificationServiceExtension.release.xcconfig"; path = "Target Support Files/Pods-exampleApp-MindboxNotificationServiceExtension/Pods-exampleApp-MindboxNotificationServiceExtension.release.xcconfig"; sourceTree = ""; }; 225AB1FB2853D87400D56406 /* libPods-exampleApp.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-exampleApp.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - 3A027BAB2C3BF24F005415BB /* NotificationModule.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationModule.swift; sourceTree = ""; }; - 3A027BAD2C3BFC4D005415BB /* NotificationModule.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NotificationModule.m; sourceTree = ""; }; + 3A027BAB2C3BF24F005415BB /* NotificationCenterStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenterStorage.swift; sourceTree = ""; }; + 3A027BAD2C3BFC4D005415BB /* NotificationModule.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = NotificationModule.mm; sourceTree = ""; }; 3A175C322C3D16C70027776A /* PushAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PushAction.swift; sourceTree = ""; }; 3A175C342C3D16F50027776A /* MindboxRemoteMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MindboxRemoteMessage.swift; sourceTree = ""; }; 3A88FC082B6BDF360046E687 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -195,8 +195,8 @@ isa = PBXGroup; children = ( 3A175C312C3D16B20027776A /* Models */, - 3A027BAD2C3BFC4D005415BB /* NotificationModule.m */, - 3A027BAB2C3BF24F005415BB /* NotificationModule.swift */, + 3A027BAD2C3BFC4D005415BB /* NotificationModule.mm */, + 3A027BAB2C3BF24F005415BB /* NotificationCenterStorage.swift */, 3A88FC0A2B6BF2840046E687 /* exampleApp-Bridging-Header.h */, 13B07FAE1A68108700A75B9A /* exampleApp */, 832341AE1AAA6A7D00B99B32 /* Libraries */, @@ -527,9 +527,9 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 3A027BAE2C3BFC4D005415BB /* NotificationModule.m in Sources */, + 3A027BAE2C3BFC4D005415BB /* NotificationModule.mm in Sources */, 3A88FC092B6BDF360046E687 /* AppDelegate.swift in Sources */, - 3A027BAC2C3BF24F005415BB /* NotificationModule.swift in Sources */, + 3A027BAC2C3BF24F005415BB /* NotificationCenterStorage.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/example/exampleApp/package.json b/example/exampleApp/package.json index f8c11e4..f6f12dd 100644 --- a/example/exampleApp/package.json +++ b/example/exampleApp/package.json @@ -41,5 +41,13 @@ }, "engines": { "node": ">=18" + }, + "codegenConfig": { + "name": "ExampleAppSpec", + "type": "modules", + "jsSrcsDir": "src", + "android": { + "javaPackageName": "com.exampleapp" + } } } diff --git a/example/exampleApp/src/native/NativeNotificationModule.ts b/example/exampleApp/src/native/NativeNotificationModule.ts new file mode 100644 index 0000000..455f3b2 --- /dev/null +++ b/example/exampleApp/src/native/NativeNotificationModule.ts @@ -0,0 +1,13 @@ +import type { TurboModule } from 'react-native' +import { TurboModuleRegistry } from 'react-native' +import type { EventEmitter } from 'react-native/Libraries/Types/CodegenTypes' + +export type NotificationCenterUpdatedEvent = {} + +export interface Spec extends TurboModule { + getNotifications(): Promise + clearNotifications(): Promise + readonly onNotificationCenterUpdated: EventEmitter +} + +export default TurboModuleRegistry.getEnforcing('NotificationModule') diff --git a/example/exampleApp/src/screens/NotificationCenterScreen.tsx b/example/exampleApp/src/screens/NotificationCenterScreen.tsx index f65a4bd..decb715 100644 --- a/example/exampleApp/src/screens/NotificationCenterScreen.tsx +++ b/example/exampleApp/src/screens/NotificationCenterScreen.tsx @@ -1,15 +1,12 @@ -import { NativeModules, NativeEventEmitter } from 'react-native' import React, { useEffect, useState } from 'react' -import { View, Text, Button, FlatList, StyleSheet } from 'react-native' +import { View, Button, FlatList } from 'react-native' import NotificationItem from '../components/NotificationItem' import { asyncOperationNCPushOpen } from '../utils/MindboxOperations' import initialNotifications from '../utils/NotificationStub' import styles from '../components/NotificationScreenStyles' import { Notification } from '../utils/Notification' import { useAppNavigation } from '../navigation/AppNavigationContext' - -const { NotificationModule } = NativeModules -const notificationEmitter = new NativeEventEmitter(NotificationModule) +import NotificationModule from '../native/NativeNotificationModule' const NotificationCenterScreen = () => { const navigation = useAppNavigation() @@ -17,11 +14,9 @@ const NotificationCenterScreen = () => { useEffect(() => { loadNotifications() - - const subscription = notificationEmitter.addListener('newNotification', () => { + const subscription = NotificationModule.onNotificationCenterUpdated(() => { loadNotifications() }) - return () => { subscription.remove() } @@ -30,7 +25,7 @@ const NotificationCenterScreen = () => { const loadNotifications = async () => { try { const result = await NotificationModule.getNotifications() - const notificationStrings = JSON.parse(result) + const notificationStrings: string[] = JSON.parse(result) const notificationList = notificationStrings.map((notificationString: string) => { const notification = JSON.parse(notificationString) /* @@ -40,9 +35,9 @@ const NotificationCenterScreen = () => { } */ if (notification.payload) { - const payload = JSON.parse(notification.payload) - notification.pushName = payload.pushName - notification.pushDate = payload.pushDate + const payload: { pushName?: string; pushDate?: string } = JSON.parse(notification.payload) + notification.pushName = payload.pushName ?? '' + notification.pushDate = payload.pushDate ?? '' } else { notification.pushName = '' notification.pushDate = ''