react-native-notify-kit 9.2.1 → 9.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -7
- package/android/build.gradle +6 -5
- package/android/proguard-rules.pro +1 -0
- package/android/src/main/baseline-prof.txt +382 -0
- package/android/src/main/java/app/notifee/core/ForegroundService.java +269 -80
- package/android/src/main/java/app/notifee/core/InitProvider.java +48 -5
- package/android/src/main/java/app/notifee/core/Logger.java +5 -0
- package/android/src/main/java/app/notifee/core/NotificationManager.java +217 -150
- package/android/src/main/java/app/notifee/core/NotificationPendingIntent.java +10 -0
- package/android/src/main/java/app/notifee/core/WarmupHelper.java +84 -0
- package/android/src/main/java/app/notifee/core/model/NotificationAndroidModel.java +17 -1
- package/android/src/main/kotlin/io/invertase/notifee/NotifeeApiModule.kt +30 -0
- package/android/src/test/java/app/notifee/core/ForegroundServiceTest.java +182 -0
- package/android/src/test/java/app/notifee/core/NotificationPendingIntentTest.java +108 -0
- package/dist/NotifeeApiModule.d.ts +1 -0
- package/dist/NotifeeApiModule.js +6 -0
- package/dist/NotifeeApiModule.js.map +1 -1
- package/dist/specs/NativeNotifeeModule.d.ts +1 -0
- package/dist/specs/NativeNotifeeModule.js.map +1 -1
- package/dist/types/Module.d.ts +19 -0
- package/dist/types/NotificationAndroid.d.ts +66 -4
- package/dist/types/NotificationAndroid.js +43 -0
- package/dist/types/NotificationAndroid.js.map +1 -1
- package/dist/utils/validate.d.ts +1 -0
- package/dist/utils/validate.js +10 -4
- package/dist/utils/validate.js.map +1 -1
- package/dist/validators/validateAndroidChannel.js +3 -3
- package/dist/validators/validateAndroidChannel.js.map +1 -1
- package/dist/validators/validateAndroidNotification.js +58 -14
- package/dist/validators/validateAndroidNotification.js.map +1 -1
- package/dist/validators/validateIOSPermissions.js +1 -1
- package/dist/validators/validateIOSPermissions.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/ios/NotifeeCore/NotifeeCore+NSNotificationCenter.m +5 -1
- package/ios/NotifeeCore/NotifeeCore+UNUserNotificationCenter.m +15 -15
- package/ios/NotifeeCore/NotifeeCore.m +41 -14
- package/ios/NotifeeCore/NotifeeCoreDelegateHolder.m +32 -18
- package/ios/NotifeeCore/NotifeeCoreExtensionHelper.m +7 -2
- package/ios/RNNotifee/NotifeeApiModule.mm +33 -11
- package/package.json +1 -1
- package/src/NotifeeApiModule.ts +7 -0
- package/src/specs/NativeNotifeeModule.ts +1 -0
- package/src/types/Module.ts +20 -0
- package/src/types/NotificationAndroid.ts +70 -4
- package/src/utils/validate.ts +12 -4
- package/src/validators/validateAndroidChannel.ts +3 -3
- package/src/validators/validateAndroidNotification.ts +64 -11
- package/src/validators/validateIOSPermissions.ts +1 -1
- package/src/version.ts +1 -1
|
@@ -28,6 +28,9 @@ static NSInteger kReactNativeNotifeeNotificationTypeTrigger = 2;
|
|
|
28
28
|
static NSInteger kReactNativeNotifeeNotificationTypeAll = 0;
|
|
29
29
|
|
|
30
30
|
@implementation NotifeeApiModule {
|
|
31
|
+
// Guarded by @synchronized(self): didReceiveNotifeeCoreEvent: may be called
|
|
32
|
+
// from arbitrary threads (UNUserNotificationCenter callbacks), while
|
|
33
|
+
// startObserving/stopObserving/sendNotifeeCoreEvent: run on the main thread.
|
|
31
34
|
bool hasListeners;
|
|
32
35
|
NSMutableArray *pendingCoreEvents;
|
|
33
36
|
}
|
|
@@ -53,15 +56,21 @@ RCT_EXPORT_MODULE();
|
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
- (void)startObserving {
|
|
56
|
-
|
|
57
|
-
|
|
59
|
+
NSArray *eventsToFlush;
|
|
60
|
+
@synchronized(self) {
|
|
61
|
+
hasListeners = YES;
|
|
62
|
+
eventsToFlush = [pendingCoreEvents copy];
|
|
63
|
+
[pendingCoreEvents removeAllObjects];
|
|
64
|
+
}
|
|
65
|
+
for (NSDictionary *eventBody in eventsToFlush) {
|
|
58
66
|
[self sendNotifeeCoreEvent:eventBody];
|
|
59
67
|
}
|
|
60
|
-
[pendingCoreEvents removeAllObjects];
|
|
61
68
|
}
|
|
62
69
|
|
|
63
70
|
- (void)stopObserving {
|
|
64
|
-
|
|
71
|
+
@synchronized(self) {
|
|
72
|
+
hasListeners = NO;
|
|
73
|
+
}
|
|
65
74
|
}
|
|
66
75
|
|
|
67
76
|
+ (BOOL)requiresMainQueueSetup {
|
|
@@ -80,18 +89,25 @@ RCT_EXPORT_MODULE();
|
|
|
80
89
|
#pragma mark - Events
|
|
81
90
|
|
|
82
91
|
- (void)didReceiveNotifeeCoreEvent:(NSDictionary *_Nonnull)event {
|
|
83
|
-
|
|
92
|
+
BOOL shouldSend;
|
|
93
|
+
@synchronized(self) {
|
|
94
|
+
shouldSend = hasListeners;
|
|
95
|
+
if (!shouldSend) {
|
|
96
|
+
[pendingCoreEvents addObject:event];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (shouldSend) {
|
|
84
100
|
[self sendNotifeeCoreEvent:event];
|
|
85
|
-
} else {
|
|
86
|
-
[pendingCoreEvents addObject:event];
|
|
87
101
|
}
|
|
88
102
|
}
|
|
89
103
|
|
|
90
104
|
- (void)sendNotifeeCoreEvent:(NSDictionary *_Nonnull)eventBody {
|
|
91
105
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
106
|
+
@synchronized(self) {
|
|
107
|
+
if (!hasListeners) {
|
|
108
|
+
[pendingCoreEvents addObject:eventBody];
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
95
111
|
}
|
|
96
112
|
// Routing foreground vs background is based on UIApplication state.
|
|
97
113
|
// iOS delivers didReceiveNotificationResponse: while the app is in
|
|
@@ -107,7 +123,8 @@ RCT_EXPORT_MODULE();
|
|
|
107
123
|
// API-triggered events require an interactive JS context. If this
|
|
108
124
|
// becomes a real-world problem, the routing should be split by event
|
|
109
125
|
// type rather than by state alone.
|
|
110
|
-
BOOL isBackground =
|
|
126
|
+
BOOL isBackground =
|
|
127
|
+
RCTRunningInAppExtension() ||
|
|
111
128
|
[UIApplication sharedApplication].applicationState != UIApplicationStateActive;
|
|
112
129
|
if (isBackground) {
|
|
113
130
|
[self sendEventWithName:kReactNativeNotifeeNotificationBackgroundEvent body:eventBody];
|
|
@@ -417,6 +434,11 @@ RCT_EXPORT_MODULE();
|
|
|
417
434
|
resolve(nil);
|
|
418
435
|
}
|
|
419
436
|
|
|
437
|
+
- (void)prewarmForegroundService:(RCTPromiseResolveBlock)resolve
|
|
438
|
+
reject:(RCTPromiseRejectBlock)reject {
|
|
439
|
+
resolve(nil);
|
|
440
|
+
}
|
|
441
|
+
|
|
420
442
|
- (void)hideNotificationDrawer {
|
|
421
443
|
// Android-only, no-op on iOS
|
|
422
444
|
}
|
package/package.json
CHANGED
package/src/NotifeeApiModule.ts
CHANGED
|
@@ -826,6 +826,13 @@ export default class NotifeeApiModule extends NotifeeNativeModule implements Mod
|
|
|
826
826
|
return this.native.stopForegroundService();
|
|
827
827
|
};
|
|
828
828
|
|
|
829
|
+
public prewarmForegroundService = (): Promise<void> => {
|
|
830
|
+
if (!isAndroid) {
|
|
831
|
+
return Promise.resolve();
|
|
832
|
+
}
|
|
833
|
+
return this.native.prewarmForegroundService();
|
|
834
|
+
};
|
|
835
|
+
|
|
829
836
|
public hideNotificationDrawer = (): void => {
|
|
830
837
|
if (!isAndroid) {
|
|
831
838
|
return;
|
|
@@ -49,6 +49,7 @@ export interface Spec extends TurboModule {
|
|
|
49
49
|
getPowerManagerInfo(): Promise<Object>;
|
|
50
50
|
openPowerManagerSettings(): Promise<void>;
|
|
51
51
|
stopForegroundService(): Promise<void>;
|
|
52
|
+
prewarmForegroundService(): Promise<void>;
|
|
52
53
|
hideNotificationDrawer(): void;
|
|
53
54
|
addListener(eventName: string): void;
|
|
54
55
|
removeListeners(count: number): void;
|
package/src/types/Module.ts
CHANGED
|
@@ -419,6 +419,26 @@ export interface Module {
|
|
|
419
419
|
*/
|
|
420
420
|
stopForegroundService(): Promise<void>;
|
|
421
421
|
|
|
422
|
+
/**
|
|
423
|
+
* Pre-warms the foreground service notification path by eagerly loading critical
|
|
424
|
+
* classes and warming the `INotificationManager` Binder proxy on a background thread.
|
|
425
|
+
*
|
|
426
|
+
* Most apps do **not** need this method — the library automatically performs warmup
|
|
427
|
+
* at app startup via `InitProvider`. This API is an escape hatch for edge cases:
|
|
428
|
+
*
|
|
429
|
+
* - Apps where the library is lazy-loaded or code-split
|
|
430
|
+
* - Apps that want to defer warmup to after the splash screen
|
|
431
|
+
* - Low-end devices where the `InitProvider` warmup hasn't finished by the time
|
|
432
|
+
* the first notification is displayed
|
|
433
|
+
*
|
|
434
|
+
* Safe to call multiple times (idempotent). On iOS this resolves immediately as a no-op.
|
|
435
|
+
*
|
|
436
|
+
* See the "Advanced / Troubleshooting" section in the README for more details.
|
|
437
|
+
*
|
|
438
|
+
* @platform android
|
|
439
|
+
*/
|
|
440
|
+
prewarmForegroundService(): Promise<void>;
|
|
441
|
+
|
|
422
442
|
/**
|
|
423
443
|
* Request specific notification permissions for your application on the current device.
|
|
424
444
|
*
|
|
@@ -262,14 +262,18 @@ export interface NotificationAndroid {
|
|
|
262
262
|
onlyAlertOnce?: boolean;
|
|
263
263
|
|
|
264
264
|
/**
|
|
265
|
-
*
|
|
266
|
-
*
|
|
267
|
-
*
|
|
265
|
+
* Controls what happens when a user presses the notification.
|
|
266
|
+
*
|
|
267
|
+
* Defaults to `{ id: 'default', launchActivity: 'default' }` when omitted,
|
|
268
|
+
* which opens the app's main activity on tap.
|
|
269
|
+
*
|
|
270
|
+
* Pass `null` explicitly to create a non-tappable notification (the notification
|
|
271
|
+
* will display but tapping it will do nothing).
|
|
268
272
|
*
|
|
269
273
|
* View the [Interaction](/react-native/android/interaction) documentation to learn
|
|
270
274
|
* more.
|
|
271
275
|
*/
|
|
272
|
-
pressAction?: NotificationPressAction;
|
|
276
|
+
pressAction?: NotificationPressAction | null;
|
|
273
277
|
|
|
274
278
|
/**
|
|
275
279
|
* The `fullScreenAction` property allows you to show a custom UI
|
|
@@ -288,6 +292,23 @@ export interface NotificationAndroid {
|
|
|
288
292
|
*/
|
|
289
293
|
foregroundServiceTypes?: AndroidForegroundServiceType[];
|
|
290
294
|
|
|
295
|
+
/**
|
|
296
|
+
* Controls the display behavior of the foreground service notification on Android 12+.
|
|
297
|
+
*
|
|
298
|
+
* When `asForegroundService` is `true` and this property is not set, it defaults to
|
|
299
|
+
* `AndroidForegroundServiceBehavior.IMMEDIATE`, which eliminates the up to 10-second
|
|
300
|
+
* display delay that Android 12+ imposes on foreground service notifications.
|
|
301
|
+
*
|
|
302
|
+
* Set to `AndroidForegroundServiceBehavior.DEFERRED` to restore the platform default
|
|
303
|
+
* deferred behavior, or `AndroidForegroundServiceBehavior.DEFAULT` for the framework-neutral
|
|
304
|
+
* default.
|
|
305
|
+
*
|
|
306
|
+
* Ignored when `asForegroundService` is `false` or not set.
|
|
307
|
+
*
|
|
308
|
+
* @platform android API 31+
|
|
309
|
+
*/
|
|
310
|
+
foregroundServiceBehavior?: AndroidForegroundServiceBehavior;
|
|
311
|
+
|
|
291
312
|
/**
|
|
292
313
|
* Set a notification importance for devices without channel support.
|
|
293
314
|
*
|
|
@@ -1476,3 +1497,48 @@ export enum AndroidForegroundServiceType {
|
|
|
1476
1497
|
FOREGROUND_SERVICE_TYPE_SYSTEM_EXEMPTED = 1024,
|
|
1477
1498
|
FOREGROUND_SERVICE_TYPE_MANIFEST = -1,
|
|
1478
1499
|
}
|
|
1500
|
+
|
|
1501
|
+
/**
|
|
1502
|
+
* Controls the display behavior of foreground service notifications on Android 12+.
|
|
1503
|
+
*
|
|
1504
|
+
* Values are aligned with androidx.core.app.NotificationCompat constants:
|
|
1505
|
+
* DEFAULT = NotificationCompat.FOREGROUND_SERVICE_DEFAULT (0)
|
|
1506
|
+
* IMMEDIATE = NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE (1)
|
|
1507
|
+
* DEFERRED = NotificationCompat.FOREGROUND_SERVICE_DEFERRED (2)
|
|
1508
|
+
*
|
|
1509
|
+
* Do not change these numeric values without updating the native getter in
|
|
1510
|
+
* NotificationAndroidModel.java.
|
|
1511
|
+
*
|
|
1512
|
+
* @platform android
|
|
1513
|
+
*/
|
|
1514
|
+
export enum AndroidForegroundServiceBehavior {
|
|
1515
|
+
/**
|
|
1516
|
+
* Framework default. Equivalent to not calling `setForegroundServiceBehavior()` at all.
|
|
1517
|
+
* On Android 12+, the system may defer display of the foreground service notification
|
|
1518
|
+
* by up to 10 seconds unless an exemption applies.
|
|
1519
|
+
*
|
|
1520
|
+
* Corresponds to `NotificationCompat.FOREGROUND_SERVICE_DEFAULT`.
|
|
1521
|
+
*/
|
|
1522
|
+
DEFAULT = 0,
|
|
1523
|
+
|
|
1524
|
+
/**
|
|
1525
|
+
* The foreground service notification is shown immediately after the service starts.
|
|
1526
|
+
* Eliminates the 10-second display delay imposed by Android 12+ on foreground service
|
|
1527
|
+
* notifications.
|
|
1528
|
+
*
|
|
1529
|
+
* This is the library default when `asForegroundService` is `true` and
|
|
1530
|
+
* `foregroundServiceBehavior` is not explicitly set.
|
|
1531
|
+
*
|
|
1532
|
+
* Corresponds to `NotificationCompat.FOREGROUND_SERVICE_IMMEDIATE`.
|
|
1533
|
+
*/
|
|
1534
|
+
IMMEDIATE = 1,
|
|
1535
|
+
|
|
1536
|
+
/**
|
|
1537
|
+
* The foreground service notification display may be deferred by up to 10 seconds by the
|
|
1538
|
+
* system. This matches the default Android 12+ behavior and the (unintended) behavior of
|
|
1539
|
+
* upstream `@notifee/react-native`.
|
|
1540
|
+
*
|
|
1541
|
+
* Corresponds to `NotificationCompat.FOREGROUND_SERVICE_DEFERRED`.
|
|
1542
|
+
*/
|
|
1543
|
+
DEFERRED = 2,
|
|
1544
|
+
}
|
package/src/utils/validate.ts
CHANGED
|
@@ -82,9 +82,17 @@ export function isValidUrl(url: string): boolean {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
export function isValidEnum(value: any, enumType: Record<string, any>): boolean {
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
// Filter out reverse-mapped string keys that TypeScript adds to numeric enums.
|
|
86
|
+
// In a numeric enum { A=0, B=1 }, Object.values() returns [0, 1, "A", "B"].
|
|
87
|
+
// The reverse-mapped string "A" satisfies typeof enumType["A"] === 'number',
|
|
88
|
+
// whereas real string enum values (e.g., TimeUnit.SECONDS = 'SECONDS')
|
|
89
|
+
// satisfy typeof enumType["SECONDS"] === 'string', so they are kept.
|
|
90
|
+
const values = Object.values(enumType).filter(
|
|
91
|
+
v => typeof v !== 'string' || typeof enumType[v] !== 'number',
|
|
92
|
+
);
|
|
93
|
+
return values.includes(value);
|
|
94
|
+
}
|
|
88
95
|
|
|
89
|
-
|
|
96
|
+
export function getNumericEnumValues(enumType: Record<string, any>): number[] {
|
|
97
|
+
return Object.values(enumType).filter((v): v is number => typeof v === 'number');
|
|
90
98
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Copyright (c) 2016-present Invertase Limited
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { objectHasProperty, isArray, isBoolean, isObject, isString } from '../utils';
|
|
5
|
+
import { objectHasProperty, isArray, isBoolean, isObject, isString, isValidEnum } from '../utils';
|
|
6
6
|
import { isValidColor, isValidVibratePattern } from './validate';
|
|
7
7
|
|
|
8
8
|
import { AndroidImportance } from '../types/NotificationAndroid';
|
|
@@ -121,7 +121,7 @@ export default function validateAndroidChannel(channel: AndroidChannel): Android
|
|
|
121
121
|
* importance
|
|
122
122
|
*/
|
|
123
123
|
if (objectHasProperty(channel, 'importance') && channel.importance !== undefined) {
|
|
124
|
-
if (!
|
|
124
|
+
if (!isValidEnum(channel.importance, AndroidImportance)) {
|
|
125
125
|
throw new Error("'channel.importance' expected an Importance value.");
|
|
126
126
|
}
|
|
127
127
|
|
|
@@ -149,7 +149,7 @@ export default function validateAndroidChannel(channel: AndroidChannel): Android
|
|
|
149
149
|
* visibility
|
|
150
150
|
*/
|
|
151
151
|
if (objectHasProperty(channel, 'visibility') && channel.visibility !== undefined) {
|
|
152
|
-
if (!
|
|
152
|
+
if (!isValidEnum(channel.visibility, AndroidVisibility)) {
|
|
153
153
|
throw new Error("'channel.visibility' expected visibility to be an AndroidVisibility value.");
|
|
154
154
|
}
|
|
155
155
|
|
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
isString,
|
|
14
14
|
isUndefined,
|
|
15
15
|
isIOS,
|
|
16
|
+
isValidEnum,
|
|
17
|
+
getNumericEnumValues,
|
|
16
18
|
} from '../utils';
|
|
17
19
|
|
|
18
20
|
import { AndroidImportance } from '../types/NotificationAndroid';
|
|
@@ -20,6 +22,7 @@ import {
|
|
|
20
22
|
AndroidBadgeIconType,
|
|
21
23
|
AndroidCategory,
|
|
22
24
|
AndroidDefaults,
|
|
25
|
+
AndroidForegroundServiceBehavior,
|
|
23
26
|
AndroidForegroundServiceType,
|
|
24
27
|
AndroidFlags,
|
|
25
28
|
AndroidGroupAlertBehavior,
|
|
@@ -116,6 +119,22 @@ export default function validateAndroidNotification(
|
|
|
116
119
|
out.asForegroundService = android.asForegroundService;
|
|
117
120
|
}
|
|
118
121
|
|
|
122
|
+
/**
|
|
123
|
+
* foregroundServiceBehavior
|
|
124
|
+
*/
|
|
125
|
+
if (
|
|
126
|
+
objectHasProperty(android, 'foregroundServiceBehavior') &&
|
|
127
|
+
!isUndefined(android.foregroundServiceBehavior)
|
|
128
|
+
) {
|
|
129
|
+
if (!isValidEnum(android.foregroundServiceBehavior, AndroidForegroundServiceBehavior)) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
"'notification.android.foregroundServiceBehavior' expected a valid AndroidForegroundServiceBehavior.",
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
out.foregroundServiceBehavior = android.foregroundServiceBehavior;
|
|
136
|
+
}
|
|
137
|
+
|
|
119
138
|
/**
|
|
120
139
|
* lightUpScreen
|
|
121
140
|
*/
|
|
@@ -153,7 +172,7 @@ export default function validateAndroidNotification(
|
|
|
153
172
|
* badgeIconType
|
|
154
173
|
*/
|
|
155
174
|
if (objectHasProperty(android, 'badgeIconType') && !isUndefined(android.badgeIconType)) {
|
|
156
|
-
if (!
|
|
175
|
+
if (!isValidEnum(android.badgeIconType, AndroidBadgeIconType)) {
|
|
157
176
|
throw new Error(
|
|
158
177
|
"'notification.android.badgeIconType' expected a valid AndroidBadgeIconType.",
|
|
159
178
|
);
|
|
@@ -239,7 +258,7 @@ export default function validateAndroidNotification(
|
|
|
239
258
|
);
|
|
240
259
|
}
|
|
241
260
|
|
|
242
|
-
const defaults =
|
|
261
|
+
const defaults = getNumericEnumValues(AndroidDefaults);
|
|
243
262
|
|
|
244
263
|
for (let i = 0; i < android.defaults.length; i++) {
|
|
245
264
|
if (!defaults.includes(android.defaults[i])) {
|
|
@@ -270,7 +289,7 @@ export default function validateAndroidNotification(
|
|
|
270
289
|
objectHasProperty(android, 'groupAlertBehavior') &&
|
|
271
290
|
!isUndefined(android.groupAlertBehavior)
|
|
272
291
|
) {
|
|
273
|
-
if (!
|
|
292
|
+
if (!isValidEnum(android.groupAlertBehavior, AndroidGroupAlertBehavior)) {
|
|
274
293
|
throw new Error(
|
|
275
294
|
"'notification.android.groupAlertBehavior' expected a valid AndroidGroupAlertBehavior.",
|
|
276
295
|
);
|
|
@@ -407,10 +426,10 @@ export default function validateAndroidNotification(
|
|
|
407
426
|
);
|
|
408
427
|
}
|
|
409
428
|
|
|
410
|
-
const
|
|
429
|
+
const foregroundServiceTypeValues = getNumericEnumValues(AndroidForegroundServiceType);
|
|
411
430
|
|
|
412
431
|
for (let i = 0; i < android.foregroundServiceTypes.length; i++) {
|
|
413
|
-
if (!
|
|
432
|
+
if (!foregroundServiceTypeValues.includes(android.foregroundServiceTypes[i])) {
|
|
414
433
|
throw new Error(
|
|
415
434
|
"'notification.android.foregroundServiceTypes' invalid array value, expected an AndroidForegroundServiceType value.",
|
|
416
435
|
);
|
|
@@ -429,13 +448,13 @@ export default function validateAndroidNotification(
|
|
|
429
448
|
}
|
|
430
449
|
|
|
431
450
|
if (android.flags.length === 0) {
|
|
432
|
-
throw new Error("'notification.android.flags' expected an array containing
|
|
451
|
+
throw new Error("'notification.android.flags' expected an array containing AndroidFlags.");
|
|
433
452
|
}
|
|
434
453
|
|
|
435
|
-
const
|
|
454
|
+
const flagValues = getNumericEnumValues(AndroidFlags);
|
|
436
455
|
|
|
437
456
|
for (let i = 0; i < android.flags.length; i++) {
|
|
438
|
-
if (!
|
|
457
|
+
if (!flagValues.includes(android.flags[i])) {
|
|
439
458
|
throw new Error(
|
|
440
459
|
"'notification.android.flags' invalid array value, expected an AndroidFlags value.",
|
|
441
460
|
);
|
|
@@ -458,13 +477,33 @@ export default function validateAndroidNotification(
|
|
|
458
477
|
|
|
459
478
|
/**
|
|
460
479
|
* pressAction
|
|
480
|
+
*
|
|
481
|
+
* Defaults to { id: 'default', launchActivity: 'default' } when omitted,
|
|
482
|
+
* so tapping the notification opens the app. Pass pressAction: null explicitly
|
|
483
|
+
* to opt out (non-tappable notification).
|
|
484
|
+
*
|
|
485
|
+
* The opt-out uses a reserved sentinel id ('__NOTIFEE_OPT_OUT__') in the bundle
|
|
486
|
+
* sent to native, so the native layer can distinguish "absent" (synthesize default)
|
|
487
|
+
* from "explicitly null" (no launch intent). The same constant is defined in
|
|
488
|
+
* NotificationPendingIntent.java as PRESS_ACTION_OPT_OUT_ID.
|
|
461
489
|
*/
|
|
462
|
-
if (
|
|
490
|
+
if (
|
|
491
|
+
objectHasProperty(android, 'pressAction') &&
|
|
492
|
+
android.pressAction !== null &&
|
|
493
|
+
!isUndefined(android.pressAction)
|
|
494
|
+
) {
|
|
463
495
|
try {
|
|
464
496
|
out.pressAction = validateAndroidPressAction(android.pressAction);
|
|
465
497
|
} catch (e: any) {
|
|
466
498
|
throw new Error(`'notification.android.pressAction' ${e.message}`);
|
|
467
499
|
}
|
|
500
|
+
} else if (android.pressAction === null) {
|
|
501
|
+
// Explicit opt-out: emit sentinel so native can distinguish from "absent".
|
|
502
|
+
// DO NOT use '__NOTIFEE_OPT_OUT__' as a real pressAction id — reserved sentinel.
|
|
503
|
+
out.pressAction = { id: '__NOTIFEE_OPT_OUT__' };
|
|
504
|
+
} else {
|
|
505
|
+
// pressAction omitted or undefined: inject default so tapping opens the app
|
|
506
|
+
out.pressAction = { id: 'default', launchActivity: 'default' };
|
|
468
507
|
}
|
|
469
508
|
|
|
470
509
|
/**
|
|
@@ -482,7 +521,7 @@ export default function validateAndroidNotification(
|
|
|
482
521
|
* importance
|
|
483
522
|
*/
|
|
484
523
|
if (objectHasProperty(android, 'importance') && !isUndefined(android.importance)) {
|
|
485
|
-
if (!
|
|
524
|
+
if (!isValidEnum(android.importance, AndroidImportance)) {
|
|
486
525
|
throw new Error("'notification.android.importance' expected a valid Importance.");
|
|
487
526
|
}
|
|
488
527
|
|
|
@@ -693,7 +732,7 @@ export default function validateAndroidNotification(
|
|
|
693
732
|
* visibility
|
|
694
733
|
*/
|
|
695
734
|
if (objectHasProperty(android, 'visibility') && android.visibility !== undefined) {
|
|
696
|
-
if (!
|
|
735
|
+
if (!isValidEnum(android.visibility, AndroidVisibility)) {
|
|
697
736
|
throw new Error(
|
|
698
737
|
"'notification.android.visibility' expected a valid AndroidVisibility value.",
|
|
699
738
|
);
|
|
@@ -739,5 +778,19 @@ export default function validateAndroidNotification(
|
|
|
739
778
|
out.ongoing = true;
|
|
740
779
|
}
|
|
741
780
|
|
|
781
|
+
/**
|
|
782
|
+
* Auto-set foregroundServiceBehavior to IMMEDIATE for foreground service notifications.
|
|
783
|
+
* Only set when asForegroundService is true and the user did not provide an explicit value.
|
|
784
|
+
* When asForegroundService is false/undefined, strip the property entirely — it has no effect
|
|
785
|
+
* on non-FGS notifications and including it would bloat the Bundle unnecessarily.
|
|
786
|
+
*/
|
|
787
|
+
if (out.asForegroundService) {
|
|
788
|
+
if (android && !objectHasProperty(android, 'foregroundServiceBehavior')) {
|
|
789
|
+
out.foregroundServiceBehavior = AndroidForegroundServiceBehavior.IMMEDIATE;
|
|
790
|
+
}
|
|
791
|
+
} else {
|
|
792
|
+
delete out.foregroundServiceBehavior;
|
|
793
|
+
}
|
|
794
|
+
|
|
742
795
|
return out;
|
|
743
796
|
}
|
|
@@ -28,7 +28,7 @@ export default function validateIOSPermissions(
|
|
|
28
28
|
|
|
29
29
|
if (objectHasProperty(permissions, 'badge')) {
|
|
30
30
|
if (!isBoolean(permissions.badge)) {
|
|
31
|
-
throw new Error("'
|
|
31
|
+
throw new Error("'badge' expected a boolean value.");
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
out.badge = permissions.badge;
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by genversion.
|
|
2
|
-
export const version = '9.
|
|
2
|
+
export const version = '9.4.0';
|