Skip to content

Commit

Permalink
Add ability to repeat notifications like on iOS (#268)
Browse files Browse the repository at this point in the history
Co-authored-by: Jesse Katsumata <jesse.katsumata@gmail.com>
Co-authored-by: Jesse Katsumata <niconico.clarinet@gmail.com>
  • Loading branch information
3 people authored Aug 20, 2021
1 parent c79a50e commit b28a99c
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 17 deletions.
35 changes: 34 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,14 +324,47 @@ request is an object containing:
- `body` : The message displayed in the notification alert.
- `badge` The number to display as the app's icon badge. Setting the number to 0 removes the icon badge.
- `fireDate` : The date and time when the system should deliver the notification.
- `repeats` : Sets notification to repeat daily. Must be used with fireDate.
- `repeats` : Sets notification to repeat. Must be used with fireDate and repeatsComponent.
- `repeatsComponent`: An object indicating which parts of fireDate should be repeated.
- `sound` : The sound played when the notification is fired.
- `category` : The category of this notification, required for actionable notifications.
- `isSilent` : If true, the notification will appear without sound.
- `isCritical` : If true, the notification sound be played even when the device is locked, muted, or has Do Not Disturb enabled.
- `criticalSoundVolume` : A number between 0 and 1 for volume of critical notification. Default volume will be used if not specified.
- `userInfo` : An object containing additional notification data.

request.repeatsComponent is an object containing (each field is optionnal):

- `month`: Will repeat every selected month in your fireDate.
- `day`: Will repeat every selected day in your fireDate.
- `hour`: Will repeat every selected hour in your fireDate.
- `minute`: Will repeat every selected minute in your fireDate.
- `second`: Will repeat every selected second in your fireDate.
- `nanosecond`: Will repeat every selected nanosecond in your fireDate.

For example, let’s say you want to have a notification repeating every day at 23:54, starting tomorrow, you will use something like this:

```javascript
const getCorrectDate = () => {
const date = new Date();
date.setDate(date.getDate() + 1);
date.setHours(23);
date.setMinutes(54);
return date;
};

PushNotificationIOS.addNotificationRequest({
fireDate: getCorrectDate(),
repeats: true,
repeatsComponent: {
hour: true,
minute: true,
},
});
```

If you want to repeat every time the clock reach 54 minutes (like 00:54, 01:54, and so on), just switch hour to false. Every field is used to indicate at what time the notification should be repeated, exactly like you could do on iOS.

---

### `setNotificationCategories()`
Expand Down
50 changes: 35 additions & 15 deletions ios/RCTConvert+Notification.m
Original file line number Diff line number Diff line change
Expand Up @@ -93,25 +93,32 @@ @implementation RCTConvert (UNNotificationRequest)
+ (UNNotificationRequest *)UNNotificationRequest:(id)json
{
NSDictionary<NSString *, id> *details = [self NSDictionary:json];

BOOL isSilent = [RCTConvert BOOL:details[@"isSilent"]];
BOOL isCritical = [RCTConvert BOOL:details[@"isCritical"]];
float criticalSoundVolume = [RCTConvert float:details[@"criticalSoundVolume"]];
NSString* identifier = [RCTConvert NSString:details[@"id"]];



UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init];
content.title= [RCTConvert NSString:details[@"title"]];
content.subtitle= [RCTConvert NSString:details[@"subtitle"]];
content.body =[RCTConvert NSString:details[@"body"]];
content.badge = [RCTConvert NSNumber:details[@"badge"]];
content.title = [RCTConvert NSString:details[@"title"]];
content.subtitle = [RCTConvert NSString:details[@"subtitle"]];
content.body = [RCTConvert NSString:details[@"body"]];
content.badge = [RCTConvert NSNumber:details[@"badge"]];
content.categoryIdentifier = [RCTConvert NSString:details[@"category"]];

NSString* threadIdentifier = [RCTConvert NSString:details[@"threadId"]];
if (threadIdentifier){
content.threadIdentifier = threadIdentifier;
}

NSDictionary<NSString *, id> *userDateComps = [RCTConvert NSDictionary:details[@"repeatsComponent"]];
BOOL month = [RCTConvert BOOL:userDateComps[@"month"]];
BOOL day = [RCTConvert BOOL:userDateComps[@"day"]];
BOOL hour = [RCTConvert BOOL:userDateComps[@"hour"]];
BOOL minute = [RCTConvert BOOL:userDateComps[@"minute"]];
BOOL second = [RCTConvert BOOL:userDateComps[@"second"]];
BOOL nanosecond = [RCTConvert BOOL:userDateComps[@"nanosecond"]];

content.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]];
if (!isSilent) {
if (isCritical) {
Expand All @@ -122,18 +129,31 @@ + (UNNotificationRequest *)UNNotificationRequest:(id)json
}
} else {
content.sound = [RCTConvert NSString:details[@"sound"]] ? [UNNotificationSound soundNamed:[RCTConvert NSString:details[@"sound"]]] : [UNNotificationSound defaultSound];
}
}```
}

NSDate* fireDate = [RCTConvert NSDate:details[@"fireDate"]];
BOOL repeats = [RCTConvert BOOL:details[@"repeats"]];
NSDateComponents *triggerDate = fireDate ? [[NSCalendar currentCalendar]
components:NSCalendarUnitYear +
NSCalendarUnitMonth + NSCalendarUnitDay +
NSCalendarUnitHour + NSCalendarUnitMinute +
NSCalendarUnitSecond + NSCalendarUnitTimeZone
fromDate:fireDate] : nil;

NSCalendarUnit defaultDateComponents =
NSCalendarUnitYear |
NSCalendarUnitMonth |
NSCalendarUnitDay |
NSCalendarUnitHour |
NSCalendarUnitMinute |
NSCalendarUnitSecond;
NSCalendarUnit repeatDateComponents =
(month ? NSCalendarUnitMonth : 0) |
(day ? NSCalendarUnitDay : 0) |
(hour ? NSCalendarUnitHour : 0) |
(minute ? NSCalendarUnitMinute : 0) |
(second ? NSCalendarUnitSecond : 0) |
(nanosecond ? NSCalendarUnitNanosecond : 0);
NSDateComponents *triggerDate = fireDate
? [[NSCalendar currentCalendar]
components:(repeats ? repeatDateComponents : defaultDateComponents) | NSCalendarUnitTimeZone
fromDate:fireDate]
: nil;

UNCalendarNotificationTrigger* trigger = triggerDate ? [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:triggerDate repeats:repeats] : nil;

UNNotificationRequest* notification = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger];
Expand Down
6 changes: 5 additions & 1 deletion js/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,12 @@ class PushNotificationIOS {
request.fireDate instanceof Date
? {...request, fireDate: request.fireDate.toISOString()}
: request;
const finalRequest = {
...handledRequest,
repeatsComponent: request.repeatsComponent || {},
};

RNCPushNotificationIOS.addNotificationRequest(handledRequest);
RNCPushNotificationIOS.addNotificationRequest(finalRequest);
}

/**
Expand Down
12 changes: 12 additions & 0 deletions js/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ export type NotificationRequest = {|
* Must be used with fireDate.
*/
repeats?: boolean,
/**
* Define what components should be used in the fireDate during repeats.
* Must be used with repeats and fireDate.
*/
repeatsComponent?: {
month?: boolean,
day?: boolean,
hour?: boolean,
minute?: boolean,
second?: boolean,
nanosecond?: boolean,
},
/**
* Sets notification to be silent
*/
Expand Down

0 comments on commit b28a99c

Please sign in to comment.