diff --git a/README.md b/README.md index 1ac090d68..dc9568255 100644 --- a/README.md +++ b/README.md @@ -312,7 +312,8 @@ 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 (default daily). Must be used with fireDate. Use repeatInterval to modify repeat behaviour. +- `repeatInterval`: The interval to repeat as a string. Possible values: `minute`, `hour`, `day`, `week`, `month`, `year` (optional). Default `day` if `repeats` is true. - `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. diff --git a/index.d.ts b/index.d.ts index d59ba22a4..7920d83a4 100644 --- a/index.d.ts +++ b/index.d.ts @@ -174,6 +174,10 @@ export type NotificationRequest = { * Must be used with fireDate. */ repeats?: boolean; + /** + * The interval to repeat as a string. Possible values: minute, hour, day, week, month, year. + */ + repeatInterval?: 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year'; /** * Sets notification to be silent */ diff --git a/ios/RCTConvert+Notification.m b/ios/RCTConvert+Notification.m index 434ef80a4..75326c5b2 100644 --- a/ios/RCTConvert+Notification.m +++ b/ios/RCTConvert+Notification.m @@ -62,14 +62,14 @@ + (UILocalNotification *)UILocalNotification:(id)json + (NSDictionary *)RCTFormatLocalNotification:(UILocalNotification *)notification { NSMutableDictionary *formattedLocalNotification = [NSMutableDictionary dictionary]; - + if (notification.fireDate) { NSDateFormatter *formatter = [NSDateFormatter new]; [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"]; NSString *fireDateString = [formatter stringFromDate:notification.fireDate]; formattedLocalNotification[@"fireDate"] = fireDateString; } - + formattedLocalNotification[@"alertAction"] = RCTNullIfNil(notification.alertAction); formattedLocalNotification[@"alertTitle"] = RCTNullIfNil(notification.alertTitle); formattedLocalNotification[@"alertBody"] = RCTNullIfNil(notification.alertBody); @@ -93,11 +93,11 @@ @implementation RCTConvert (UNNotificationRequest) + (UNNotificationRequest *)UNNotificationRequest:(id)json { NSDictionary *details = [self NSDictionary:json]; - + BOOL isSilent = [RCTConvert BOOL:details[@"isSilent"]]; NSString* identifier = [RCTConvert NSString:details[@"id"]]; - - + + UNMutableNotificationContent* content = [[UNMutableNotificationContent alloc] init]; content.title= [RCTConvert NSString:details[@"title"]]; content.subtitle= [RCTConvert NSString:details[@"subtitle"]]; @@ -116,14 +116,64 @@ + (UNNotificationRequest *)UNNotificationRequest:(id)json } 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; - + NSString* repeatInterval = [RCTConvert NSString:details[@"repeatInterval"]]; + + //For backward compatability with existing request interface. + //If repeats param is set to true and no repeatInterval set then use "day" as the default + BOOL repeatsParam = [RCTConvert BOOL:details[@"repeats"]]; + if (repeatsParam && repeatInterval.length == 0) { + repeatInterval = @"day"; + } + + BOOL repeats = TRUE; + NSDateComponents *triggerDate = nil; + + if (fireDate) { + if ([repeatInterval isEqualToString:@"year"]) { + triggerDate = [[NSCalendar currentCalendar] + components: + NSCalendarUnitMonth + NSCalendarUnitDay + + NSCalendarUnitHour + NSCalendarUnitMinute + + NSCalendarUnitSecond + NSCalendarUnitTimeZone + fromDate:fireDate]; + } else if ([repeatInterval isEqualToString:@"month"]) { + triggerDate = fireDate ? [[NSCalendar currentCalendar] + components: NSCalendarUnitDay + + NSCalendarUnitHour + NSCalendarUnitMinute + + NSCalendarUnitSecond + NSCalendarUnitTimeZone + fromDate:fireDate] : nil; + } else if ([repeatInterval isEqualToString:@"week"]) { + triggerDate = fireDate ? [[NSCalendar currentCalendar] + components: NSCalendarUnitWeekday + + NSCalendarUnitHour + NSCalendarUnitMinute + + NSCalendarUnitSecond + NSCalendarUnitTimeZone + fromDate:fireDate] : nil; + } else if ([repeatInterval isEqualToString:@"day"]) { + triggerDate = fireDate ? [[NSCalendar currentCalendar] + components: + NSCalendarUnitHour + NSCalendarUnitMinute + + NSCalendarUnitSecond + NSCalendarUnitTimeZone + fromDate:fireDate] : nil; + } else if ([repeatInterval isEqualToString:@"hour"]) { + triggerDate = fireDate ? [[NSCalendar currentCalendar] + components: NSCalendarUnitMinute + + NSCalendarUnitSecond + NSCalendarUnitTimeZone + fromDate:fireDate] : nil; + } else if ([repeatInterval isEqualToString:@"minute"]) { + triggerDate = fireDate ? [[NSCalendar currentCalendar] + components: NSCalendarUnitSecond + NSCalendarUnitTimeZone + fromDate:fireDate] : nil; + } else { //If no valid repeat interval, set repeats to false and create non-repeating trigger date + repeats = FALSE; + triggerDate = [[NSCalendar currentCalendar] + components: NSCalendarUnitYear + + NSCalendarUnitMonth + NSCalendarUnitDay + + NSCalendarUnitHour + NSCalendarUnitMinute + + NSCalendarUnitSecond + NSCalendarUnitTimeZone + fromDate:fireDate]; + } + } + UNCalendarNotificationTrigger* trigger = triggerDate ? [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:triggerDate repeats:repeats] : nil; UNNotificationRequest* notification = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger]; @@ -136,9 +186,9 @@ + (UNNotificationRequest *)UNNotificationRequest:(id)json + (NSDictionary *)RCTFormatUNNotificationRequest:(UNNotificationRequest*)request { NSMutableDictionary *formattedRequest = [NSMutableDictionary dictionary]; - + formattedRequest[@"id"] = RCTNullIfNil(request.identifier); - + UNNotificationContent *content = request.content; formattedRequest[@"title"] = RCTNullIfNil(content.title); formattedRequest[@"subtitle"] = RCTNullIfNil(content.subtitle); @@ -148,7 +198,7 @@ + (NSDictionary *)RCTFormatUNNotificationRequest:(UNNotificationRequest*)request formattedRequest[@"category"] = RCTNullIfNil(content.categoryIdentifier); formattedRequest[@"thread-id"] = RCTNullIfNil(content.threadIdentifier); formattedRequest[@"userInfo"] = RCTNullIfNil(RCTJSONClean(content.userInfo)); - + if (request.trigger) { UNCalendarNotificationTrigger* trigger = (UNCalendarNotificationTrigger*)request.trigger; NSDateFormatter *formatter = [NSDateFormatter new]; @@ -195,11 +245,11 @@ + (UNNotificationAction *)UNNotificationAction:(id)json NSDictionary *details = [self NSDictionary:json]; NSString* identifier = [RCTConvert NSString:details[@"id"]]; NSString* title = [RCTConvert NSString:details[@"title"]]; - - + + UNNotificationActionOptions options = [RCTConvert UNNotificationActionOptions:details[@"options"]]; UNNotificationAction* action = details[@"textInput"] ? [UNTextInputNotificationAction actionWithIdentifier:identifier title:title options:options textInputButtonTitle:details[@"textInput"][@"buttonTitle"] textInputPlaceholder:details[@"textInput"][@"placeholder"]] : [UNNotificationAction actionWithIdentifier:identifier title:title options:options]; - + return action; } @@ -213,15 +263,15 @@ @implementation RCTConvert (UNNotificationCategory) + (UNNotificationCategory *)UNNotificationCategory:(id)json { NSDictionary *details = [self NSDictionary:json]; - + NSString* identifier = [RCTConvert NSString:details[@"id"]]; NSMutableArray* actions = [NSMutableArray new]; for (NSDictionary* action in [RCTConvert NSArray:details[@"actions"]]) { [actions addObject:[RCTConvert UNNotificationAction:action]]; } - + UNNotificationCategory* category = [UNNotificationCategory categoryWithIdentifier:identifier actions:actions intentIdentifiers:@[] options:UNNotificationCategoryOptionNone]; - + return category; } @@ -237,21 +287,21 @@ + (NSDictionary *)RCTFormatUNNotificationResponse:(UNNotificationResponse *)resp UNNotification* notification = response.notification; NSMutableDictionary *formattedResponse = [[RCTConvert RCTFormatUNNotification:notification] mutableCopy]; UNNotificationContent *content = notification.request.content; - + NSMutableDictionary *userInfo = [content.userInfo mutableCopy]; userInfo[@"userInteraction"] = [NSNumber numberWithInt:1]; userInfo[@"actionIdentifier"] = response.actionIdentifier; - + formattedResponse[@"badge"] = RCTNullIfNil(content.badge); formattedResponse[@"sound"] = RCTNullIfNil(content.sound); formattedResponse[@"userInfo"] = RCTNullIfNil(RCTJSONClean(userInfo)); formattedResponse[@"actionIdentifier"] = RCTNullIfNil(response.actionIdentifier); - + NSString* userText = [response isKindOfClass:[UNTextInputNotificationResponse class]] ? ((UNTextInputNotificationResponse *)response).userText : nil; if (userText) { formattedResponse[@"userText"] = RCTNullIfNil(userText); } - + return formattedResponse; } @end @@ -265,16 +315,16 @@ + (NSDictionary *)RCTFormatUNNotification:(UNNotification *)notification { NSMutableDictionary *formattedNotification = [NSMutableDictionary dictionary]; UNNotificationContent *content = notification.request.content; - + formattedNotification[@"identifier"] = notification.request.identifier; - + if (notification.date) { NSDateFormatter *formatter = [NSDateFormatter new]; [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"]; NSString *dateString = [formatter stringFromDate:notification.date]; formattedNotification[@"date"] = dateString; } - + formattedNotification[@"title"] = RCTNullIfNil(content.title); formattedNotification[@"subtitle"] = RCTNullIfNil(content.subtitle); formattedNotification[@"body"] = RCTNullIfNil(content.body); @@ -283,7 +333,7 @@ + (NSDictionary *)RCTFormatUNNotification:(UNNotification *)notification formattedNotification[@"category"] = RCTNullIfNil(content.categoryIdentifier); formattedNotification[@"thread-id"] = RCTNullIfNil(content.threadIdentifier); formattedNotification[@"userInfo"] = RCTNullIfNil(RCTJSONClean(content.userInfo)); - + return formattedNotification; } diff --git a/js/types.js b/js/types.js index c373e0de8..bd2584c7c 100644 --- a/js/types.js +++ b/js/types.js @@ -45,6 +45,10 @@ export type NotificationRequest = {| * Must be used with fireDate. */ repeats?: boolean, + /** + * The interval to repeat as a string. Possible values: minute, hour, day, week, month, year. + */ + repeatInterval?: string, /** * Sets notification to be silent */