react-native-notify-kit 10.0.0 → 10.2.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 (44) hide show
  1. package/README.md +168 -100
  2. package/android/build.gradle +2 -2
  3. package/android/src/androidTest/java/app/notifee/core/RebootRecoveryTest.java +94 -2
  4. package/android/src/main/AndroidManifest.xml +8 -7
  5. package/android/src/main/java/app/notifee/core/ForegroundService.java +1 -0
  6. package/android/src/main/java/app/notifee/core/NotificationManager.java +8 -0
  7. package/android/src/main/java/app/notifee/core/RebootBroadcastReceiver.java +16 -0
  8. package/android/src/main/java/app/notifee/core/model/NotificationAndroidModel.java +8 -3
  9. package/android/src/main/java/app/notifee/core/model/TimestampTriggerModel.java +42 -27
  10. package/android/src/main/java/app/notifee/core/utility/ResourceUtils.java +30 -1
  11. package/android/src/main/kotlin/io/invertase/notifee/NotifeeApiModule.kt +6 -6
  12. package/android/src/test/java/app/notifee/core/model/NotificationAndroidModelSmallIconFallbackTest.java +74 -0
  13. package/android/src/test/java/app/notifee/core/model/TimestampTriggerModelTest.java +154 -0
  14. package/android/src/test/java/app/notifee/core/utility/ResourceUtilsFallbackIconTest.java +116 -0
  15. package/dist/types/Module.d.ts +7 -7
  16. package/dist/types/Notification.d.ts +1 -1
  17. package/dist/types/NotificationIOS.d.ts +44 -21
  18. package/dist/types/NotificationIOS.js +15 -1
  19. package/dist/types/NotificationIOS.js.map +1 -1
  20. package/dist/types/Trigger.d.ts +45 -2
  21. package/dist/types/Trigger.js +22 -0
  22. package/dist/types/Trigger.js.map +1 -1
  23. package/dist/validators/validateIOSCategory.js +2 -2
  24. package/dist/validators/validateIOSCategory.js.map +1 -1
  25. package/dist/validators/validateTrigger.js +42 -1
  26. package/dist/validators/validateTrigger.js.map +1 -1
  27. package/dist/version.d.ts +1 -1
  28. package/dist/version.js +1 -1
  29. package/ios/NotifeeCore/NotifeeCore+NSNotificationCenter.m +20 -10
  30. package/ios/NotifeeCore/NotifeeCore+UNUserNotificationCenter.h +1 -2
  31. package/ios/NotifeeCore/NotifeeCore+UNUserNotificationCenter.m +44 -35
  32. package/ios/NotifeeCore/NotifeeCore.m +1035 -99
  33. package/ios/NotifeeCore/NotifeeCoreUtil.h +36 -1
  34. package/ios/NotifeeCore/NotifeeCoreUtil.m +533 -9
  35. package/ios/RNNotifee/NotifeeApiModule.mm +2 -2
  36. package/package.json +1 -1
  37. package/server/README.md +4 -4
  38. package/src/types/Module.ts +7 -7
  39. package/src/types/Notification.ts +1 -1
  40. package/src/types/NotificationIOS.ts +43 -28
  41. package/src/types/Trigger.ts +45 -1
  42. package/src/validators/validateIOSCategory.ts +4 -2
  43. package/src/validators/validateTrigger.ts +62 -0
  44. package/src/version.ts +1 -1
@@ -543,20 +543,20 @@ export interface Module {
543
543
  /**
544
544
  * API used to configure iOS notification handling behavior.
545
545
  *
546
- * Use this to control whether Notifee should handle remote (push) notifications
546
+ * Use this to control whether Notify Kit should handle remote (push) notifications
547
547
  * on iOS. When `handleRemoteNotifications` is set to `false`, remote notifications
548
548
  * (e.g. from Firebase Cloud Messaging) will be forwarded to the original
549
- * notification delegate instead of being processed by Notifee.
549
+ * notification delegate instead of being processed by Notify Kit.
550
550
  *
551
551
  * This allows libraries like React Native Firebase Messaging to receive tap events
552
552
  * via `onNotificationOpenedApp()` and `getInitialNotification()`.
553
553
  *
554
- * Defaults to `true` (Notifee handles all notifications) for backward compatibility.
554
+ * Defaults to `true` (Notify Kit handles all notifications) for backward compatibility.
555
555
  *
556
556
  * ```js
557
557
  * import notifee from 'react-native-notify-kit';
558
558
  *
559
- * // Disable Notifee handling of remote notifications
559
+ * // Disable Notify Kit handling of remote notifications
560
560
  * await notifee.setNotificationConfig({
561
561
  * ios: { handleRemoteNotifications: false },
562
562
  * });
@@ -663,8 +663,8 @@ export interface Module {
663
663
  hideNotificationDrawer(): void;
664
664
 
665
665
  /**
666
- * Processes an FCM remote message produced by the NotifyKit server SDK and
667
- * displays a Notifee notification according to the embedded `notifee_options`.
666
+ * Processes an FCM remote message produced by the Notify Kit server SDK and
667
+ * displays a Notify Kit notification according to the embedded `notifee_options`.
668
668
  *
669
669
  * Safe to call from both `setBackgroundMessageHandler` and `onMessage`.
670
670
  *
@@ -696,7 +696,7 @@ export interface Module {
696
696
  */
697
697
  export interface ModuleStatics {
698
698
  /**
699
- * Returns the current Notifee SDK version in use.
699
+ * Returns the current Notify Kit SDK version in use.
700
700
  */
701
701
  SDK_VERSION: string;
702
702
  }
@@ -192,7 +192,7 @@ export interface TriggerNotification {
192
192
  }
193
193
 
194
194
  /**
195
- * An interface representing a Notifee event.
195
+ * An interface representing a Notify Kit event.
196
196
  *
197
197
  * View the [Events](/react-native/events) documentation to learn more about foreground and
198
198
  * background events.
@@ -79,25 +79,20 @@ export interface NotificationIOS {
79
79
  threadId?: string;
80
80
 
81
81
  /**
82
- * The argument that is inserted in the IOSCategory.summaryFormat for this notification.
83
- *
84
- * See `IOSCategory.summaryFormat`.
82
+ * Kept for compatibility. No-op on supported iOS versions because notification summary
83
+ * arguments are ignored by iOS 15+.
85
84
  *
86
85
  * @platform ios iOS >= 12
86
+ * @deprecated May be removed in a future major release.
87
87
  */
88
88
  summaryArgument?: string;
89
89
 
90
90
  /**
91
- * A number that indicates how many items in the summary are being represented.
92
- *
93
- * For example if a messages app sends one notification for 3 new messages in a group chat,
94
- * the summaryArgument could be the name of the group chat and the summaryArgumentCount should be 3.
95
- *
96
- * If set, value cannot be 0 or less.
97
- *
98
- * See `IOSCategory.summaryFormat`.
91
+ * Kept for compatibility. No-op on supported iOS versions because notification summary
92
+ * arguments are ignored by iOS 15+.
99
93
  *
100
94
  * @platform ios iOS >= 12
95
+ * @deprecated May be removed in a future major release.
101
96
  */
102
97
  summaryArgumentCount?: number;
103
98
 
@@ -113,7 +108,7 @@ export interface NotificationIOS {
113
108
  /**
114
109
  * Optional property to customise how notifications are presented when the app is in the foreground.
115
110
  *
116
- * By default, Notifee will show iOS notifications in heads-up mode if your app is currently in the foreground.
111
+ * By default, Notify Kit will show iOS notifications in heads-up mode if your app is currently in the foreground.
117
112
  */
118
113
  foregroundPresentationOptions?: IOSForegroundPresentationOptions;
119
114
 
@@ -147,7 +142,7 @@ export interface IOSCommunicationInfoPerson {
147
142
  /**
148
143
  * An interface to customise how notifications are shown when the app is in the foreground.
149
144
  *
150
- * By default, Notifee will show iOS notifications in heads-up mode if your app is currently in the foreground.
145
+ * By default, Notify Kit will show iOS notifications in heads-up mode if your app is currently in the foreground.
151
146
  *
152
147
  * View the [Foreground Notifications](/react-native/ios/appearance#foreground-notifications) to learn
153
148
  * more.
@@ -180,8 +175,6 @@ export interface IOSForegroundPresentationOptions {
180
175
  /**
181
176
  * Present the notification as a banner
182
177
  *
183
- * For iOS 13 and lower, will be equivalent to setting `alert` to true
184
- *
185
178
  * Defaults to true
186
179
  */
187
180
  banner?: boolean;
@@ -189,8 +182,6 @@ export interface IOSForegroundPresentationOptions {
189
182
  /**
190
183
  * Show the notification in Notification Center
191
184
  *
192
- * For iOS 13 and lower, will be equivalent to setting `alert` to true
193
- *
194
185
  * Defaults to true
195
186
  */
196
187
  list?: boolean;
@@ -254,18 +245,20 @@ export interface IOSNotificationPermissions {
254
245
  provisional?: boolean;
255
246
 
256
247
  /**
257
- * Request permission for Siri to automatically read out notification messages over AirPods.
248
+ * Explicit announcement authorization requests are no longer needed because announcement
249
+ * authorization is included by iOS on supported versions. Kept for API compatibility.
258
250
  *
259
251
  * Defaults to false.
260
252
  *
261
253
  * @platform ios iOS >= 13
254
+ * @deprecated May be removed in a future major release.
262
255
  */
263
256
  announcement?: boolean;
264
257
 
265
258
  // TODO later version
266
259
  // /**
267
260
  // * Using this permission indicates to iOS that it should display a button for in-app notification
268
- // * settings. Pressing this button when your application is open will trigger a Notifee
261
+ // * settings. Pressing this button when your application is open will trigger a Notify Kit
269
262
  // * 'ACTION_PRESS' event with a `pressAction.id` of 'notification-settings'
270
263
  // * (or via getInitialNotification 'pressAction.id' when app launched)
271
264
  // *
@@ -402,13 +395,28 @@ export interface IOSNotificationSettings {
402
395
  }
403
396
 
404
397
  /**
405
- * TODO docs, used to provide context to Siri
398
+ * Intent identifiers used to provide category context to Siri.
406
399
  *
407
400
  * @platform ios
408
401
  */
409
402
  export enum IOSIntentIdentifier {
403
+ /**
404
+ * Start a call intent.
405
+ *
406
+ * Maps to Apple's unified `INStartCallIntentIdentifier`.
407
+ */
408
+ START_CALL = 25,
409
+
410
+ /**
411
+ * @deprecated Use `START_CALL` instead. Apple deprecated the separate audio call
412
+ * intent identifier in favor of a unified start call identifier.
413
+ */
410
414
  START_AUDIO_CALL = 0,
411
415
 
416
+ /**
417
+ * @deprecated Use `START_CALL` instead. Apple deprecated the separate video call
418
+ * intent identifier in favor of a unified start call identifier.
419
+ */
412
420
  START_VIDEO_CALL = 1,
413
421
 
414
422
  SEARCH_CALL_HISTORY = 2,
@@ -487,14 +495,13 @@ export interface IOSNotificationCategory {
487
495
  */
488
496
  allowInCarPlay?: boolean;
489
497
 
490
- /*
491
- * Allow notifications in this category to be announced to the user
492
- * via 3rd party services such as Siri.
493
- *
494
- * For example, if the notification can be automatically read by Siri
495
- * while the user is wearing AirPods.
498
+ /**
499
+ * Kept for compatibility. No-op on supported iOS versions because announcement category
500
+ * options are ignored by iOS 15+.
496
501
  *
497
502
  * Defaults to `false`.
503
+ *
504
+ * @deprecated May be removed in a future major release.
498
505
  */
499
506
  allowAnnouncement?: boolean;
500
507
 
@@ -519,6 +526,14 @@ export interface IOSNotificationCategory {
519
526
  */
520
527
  hiddenPreviewsBodyPlaceholder?: string;
521
528
 
529
+ /**
530
+ * Intent identifiers used by the system to provide category context to Siri.
531
+ *
532
+ * Use `IOSIntentIdentifier.START_CALL` for call categories. Legacy
533
+ * `START_AUDIO_CALL` and `START_VIDEO_CALL` values remain accepted for source
534
+ * compatibility, but both map to Apple's unified start call intent identifier.
535
+ * Categories read back from iOS can return `START_CALL` for call intents.
536
+ */
522
537
  intentIdentifiers?: IOSIntentIdentifier[];
523
538
 
524
539
  /*
@@ -689,11 +704,11 @@ export type IOSNotificationInterruptionLevel = 'active' | 'critical' | 'passive'
689
704
  */
690
705
  export interface IOSNotificationConfig {
691
706
  /**
692
- * Whether Notifee should handle remote (push) notifications on iOS.
707
+ * Whether Notify Kit should handle remote (push) notifications on iOS.
693
708
  *
694
709
  * When set to `false`, remote notifications (e.g. from Firebase Cloud Messaging)
695
710
  * will be forwarded to the original notification delegate instead of being
696
- * processed by Notifee. This allows libraries like React Native Firebase
711
+ * processed by Notify Kit. This allows libraries like React Native Firebase
697
712
  * Messaging to receive tap events via `onNotificationOpenedApp()` and
698
713
  * `getInitialNotification()`.
699
714
  *
@@ -21,13 +21,35 @@ export interface TimestampTrigger {
21
21
  * if set to `RepeatFrequency.HOURLY`, the notification will repeat every hour from the timestamp specified.
22
22
  * if set to `RepeatFrequency.DAILY`, the notification will repeat every day from the timestamp specified.
23
23
  * if set to `RepeatFrequency.WEEKLY`, the notification will repeat every week from the timestamp specified.
24
+ * if set to `RepeatFrequency.MONTHLY`, the notification will repeat every calendar month from the timestamp specified.
25
+ *
26
+ * Yearly recurrence is not supported.
24
27
  */
25
28
  repeatFrequency?: RepeatFrequency;
26
29
 
30
+ /**
31
+ * Multiplier applied to `repeatFrequency` for timestamp triggers.
32
+ *
33
+ * For example, `repeatFrequency: RepeatFrequency.DAILY` with
34
+ * `repeatInterval: 2` repeats every 2 days. `RepeatFrequency.WEEKLY`
35
+ * with `repeatInterval: 2` repeats every 2 weeks, and
36
+ * `RepeatFrequency.MONTHLY` with `repeatInterval: 3` repeats every
37
+ * 3 months.
38
+ *
39
+ * Valid only when `repeatFrequency` is set to a repeating value. It is
40
+ * not valid without `repeatFrequency` or with `RepeatFrequency.NONE`.
41
+ * Must be a positive integer.
42
+ *
43
+ * Defaults to `1` when `repeatFrequency` is set.
44
+ */
45
+ repeatInterval?: number;
46
+
27
47
  /**
28
48
  * Choose to schedule your trigger notification with Android's AlarmManager API.
29
49
  *
30
- * By default, trigger notifications are created with Android's WorkManager API.
50
+ * By default, timestamp trigger notifications use AlarmManager on Android. Set
51
+ * this to `false` to opt out to WorkManager. `RepeatFrequency.MONTHLY` is not
52
+ * supported when this is `false`.
31
53
  *
32
54
  * @platform android
33
55
  */
@@ -71,14 +93,36 @@ export interface TimestampTriggerAlarmManager {
71
93
 
72
94
  /**
73
95
  * An interface representing the different frequencies which can be used with `TimestampTrigger.repeatFrequency`.
96
+ * Supported timestamp repeat frequencies are hourly, daily, weekly, and monthly.
97
+ * Yearly recurrence is not supported.
74
98
  *
75
99
  * View the [Triggers](/react-native/triggers) documentation to learn more.
76
100
  */
77
101
  export enum RepeatFrequency {
102
+ /**
103
+ * Do not repeat. `TimestampTrigger.repeatInterval` cannot be used with `NONE`.
104
+ */
78
105
  NONE = -1,
106
+ /**
107
+ * Repeat every calendar hour from the timestamp.
108
+ */
79
109
  HOURLY = 0,
110
+ /**
111
+ * Repeat every calendar day from the timestamp.
112
+ */
80
113
  DAILY = 1,
114
+ /**
115
+ * Repeat every calendar week from the timestamp.
116
+ */
81
117
  WEEKLY = 2,
118
+ /**
119
+ * Repeat every calendar month from the timestamp.
120
+ *
121
+ * Use `TimestampTrigger.repeatInterval` for intervals such as every 3 months.
122
+ * Monthly repeats use native calendar semantics; dates that do not exist in a
123
+ * target month are adjusted by the platform calendar.
124
+ */
125
+ MONTHLY = 3,
82
126
  }
83
127
 
84
128
  /**
@@ -110,12 +110,14 @@ export default function validateIOSCategory(
110
110
  throw new Error("'category.intentIdentifiers' expected an array value.");
111
111
  }
112
112
 
113
- const identifiers = Object.values(IOSIntentIdentifier);
113
+ const identifiers = Object.values(IOSIntentIdentifier).filter(
114
+ (identifier): identifier is IOSIntentIdentifier => typeof identifier === 'number',
115
+ );
114
116
 
115
117
  for (let i = 0; i < category.intentIdentifiers.length; i++) {
116
118
  const intentIdentifier = category.intentIdentifiers[i];
117
119
 
118
- if (!identifiers.includes(intentIdentifier)) {
120
+ if (typeof intentIdentifier !== 'number' || !identifiers.includes(intentIdentifier)) {
119
121
  throw new Error(
120
122
  `'category.intentIdentifiers' unexpected intentIdentifier "${intentIdentifier}" at array index "${i}".`,
121
123
  );
@@ -9,6 +9,7 @@ import {
9
9
  isValidEnum,
10
10
  isUndefined,
11
11
  isBoolean,
12
+ isAndroid,
12
13
  } from '../utils';
13
14
  import {
14
15
  Trigger,
@@ -52,11 +53,35 @@ export default function validateTrigger(trigger: Trigger): Trigger {
52
53
  }
53
54
  }
54
55
 
56
+ // Smallest plausible epoch-ms value for a future trigger. 1e12 ms ≈ Sep 2001;
57
+ // anything smaller is almost certainly the wrong unit (seconds-since-epoch,
58
+ // or a Date.getDate() day-of-month). We intercept the three common shapes
59
+ // and emit targeted hints — see upstream invertase/notifee#872 for the
60
+ // year-after-year UX pattern this guards against.
61
+ const MIN_PLAUSIBLE_EPOCH_MS = 1e12;
62
+ const MIN_PLAUSIBLE_EPOCH_SECONDS = 1e9;
63
+
55
64
  function validateTimestampTrigger(trigger: TimestampTrigger): TimestampTrigger {
56
65
  if (!isNumber(trigger.timestamp)) {
57
66
  throw new Error("'trigger.timestamp' expected a number value.");
58
67
  }
59
68
 
69
+ if (trigger.timestamp < MIN_PLAUSIBLE_EPOCH_MS) {
70
+ if (trigger.timestamp >= 1 && trigger.timestamp <= 31) {
71
+ throw new Error(
72
+ `'trigger.timestamp' looks like a day-of-month (${trigger.timestamp}). Did you mean \`date.getTime()\` instead of \`date.getDate()\`?`,
73
+ );
74
+ }
75
+ if (trigger.timestamp >= MIN_PLAUSIBLE_EPOCH_SECONDS) {
76
+ throw new Error(
77
+ `'trigger.timestamp' looks like seconds since epoch (${trigger.timestamp}). Notifee expects milliseconds — multiply by 1000, or use \`Date.now()\` / \`date.getTime()\`.`,
78
+ );
79
+ }
80
+ throw new Error(
81
+ `'trigger.timestamp' (${trigger.timestamp}) is too small to be a valid epoch millisecond value. Use \`Date.now()\` or \`someDate.getTime()\`.`,
82
+ );
83
+ }
84
+
60
85
  const now = Date.now();
61
86
  if (trigger.timestamp <= now) {
62
87
  throw new Error("'trigger.timestamp' date must be in the future.");
@@ -76,6 +101,33 @@ function validateTimestampTrigger(trigger: TimestampTrigger): TimestampTrigger {
76
101
  out.repeatFrequency = trigger.repeatFrequency;
77
102
  }
78
103
 
104
+ const hasRepeatFrequency =
105
+ objectHasProperty(trigger, 'repeatFrequency') && !isUndefined(trigger.repeatFrequency);
106
+ const isRepeating = hasRepeatFrequency && trigger.repeatFrequency !== RepeatFrequency.NONE;
107
+
108
+ if (objectHasProperty(trigger, 'repeatInterval') && !isUndefined(trigger.repeatInterval)) {
109
+ if (!hasRepeatFrequency) {
110
+ throw new Error("'trigger.repeatInterval' requires a repeatFrequency value.");
111
+ }
112
+
113
+ if (trigger.repeatFrequency === RepeatFrequency.NONE) {
114
+ throw new Error("'trigger.repeatInterval' requires a repeating repeatFrequency value.");
115
+ }
116
+
117
+ if (
118
+ !isNumber(trigger.repeatInterval) ||
119
+ !Number.isFinite(trigger.repeatInterval) ||
120
+ !Number.isInteger(trigger.repeatInterval) ||
121
+ trigger.repeatInterval <= 0
122
+ ) {
123
+ throw new Error("'trigger.repeatInterval' expected a positive integer value.");
124
+ }
125
+
126
+ out.repeatInterval = trigger.repeatInterval;
127
+ } else if (isRepeating) {
128
+ out.repeatInterval = 1;
129
+ }
130
+
79
131
  if (objectHasProperty(trigger, 'alarmManager') && !isUndefined(trigger.alarmManager)) {
80
132
  if (isBoolean(trigger.alarmManager)) {
81
133
  if (trigger.alarmManager) {
@@ -94,6 +146,16 @@ function validateTimestampTrigger(trigger: TimestampTrigger): TimestampTrigger {
94
146
  out.alarmManager = validateTimestampAlarmManager();
95
147
  }
96
148
 
149
+ if (
150
+ isAndroid &&
151
+ trigger.alarmManager === false &&
152
+ out.repeatFrequency === RepeatFrequency.MONTHLY
153
+ ) {
154
+ throw new Error(
155
+ "'trigger.repeatFrequency' MONTHLY is not supported when 'trigger.alarmManager' is false.",
156
+ );
157
+ }
158
+
97
159
  return out;
98
160
  }
99
161
 
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '10.0.0';
2
+ export const version = '10.2.0';