[notifications] Refactor for better support of separate messages
This commit is contained in:
parent
831eec82f7
commit
303cb4c428
|
@ -39,8 +39,8 @@ static NSString *const MESSAGING_TOKEN_REFRESHED = @"messaging_token_refreshed";
|
|||
static NSString *const MESSAGING_NOTIFICATION_RECEIVED = @"messaging_notification_received";
|
||||
|
||||
// Notifications
|
||||
static NSString *const NOTIFICATIONS_NOTIFICATION_CLICKED = @"notifications_notification_clicked";
|
||||
static NSString *const NOTIFICATIONS_NOTIFICATION_DISPLAYED = @"notifications_notification_displayed";
|
||||
static NSString *const NOTIFICATIONS_NOTIFICATION_PRESSED = @"notifications_notification_pressed";
|
||||
static NSString *const NOTIFICATIONS_NOTIFICATION_RECEIVED = @"notifications_notification_received";
|
||||
|
||||
// AdMob
|
||||
|
|
|
@ -15,7 +15,8 @@
|
|||
@property _Nullable RCTPromiseResolveBlock permissionResolver;
|
||||
|
||||
#if !TARGET_OS_TV
|
||||
- (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings;
|
||||
- (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo;
|
||||
- (void)didRegisterUserNotificationSettings:(nonnull UIUserNotificationSettings *)notificationSettings;
|
||||
#endif
|
||||
|
||||
@end
|
||||
|
|
|
@ -70,6 +70,12 @@ RCT_EXPORT_MODULE()
|
|||
_permissionResolver = nil;
|
||||
}
|
||||
|
||||
// Listen for FCM data messages that arrive as a remote notification
|
||||
- (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo {
|
||||
NSDictionary *message = [self parseUserInfo:userInfo];
|
||||
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_MESSAGE_RECEIVED body:message];
|
||||
}
|
||||
|
||||
// *******************************************************
|
||||
// ** Finish AppDelegate methods
|
||||
// *******************************************************
|
||||
|
@ -88,8 +94,15 @@ RCT_EXPORT_MODULE()
|
|||
|
||||
// Listen for data messages in the foreground
|
||||
- (void)applicationReceivedRemoteMessage:(nonnull FIRMessagingRemoteMessage *)remoteMessage {
|
||||
NSDictionary *message = [self parseFIRMessagingRemoteMessage:remoteMessage openedFromTray:false];
|
||||
NSDictionary *message = [self parseFIRMessagingRemoteMessage:remoteMessage];
|
||||
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_MESSAGE_RECEIVED body:message];
|
||||
}
|
||||
|
||||
// Receive data messages on iOS 10+ directly from FCM (bypassing APNs) when the app is in the foreground.
|
||||
// To enable direct data messages, you can set [Messaging messaging].shouldEstablishDirectChannel to YES.
|
||||
- (void)messaging:(nonnull FIRMessaging *)messaging
|
||||
didReceiveMessage:(nonnull FIRMessagingRemoteMessage *)remoteMessage {
|
||||
NSDictionary *message = [self parseFIRMessagingRemoteMessage:remoteMessage];
|
||||
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_MESSAGE_RECEIVED body:message];
|
||||
}
|
||||
|
||||
|
@ -250,8 +263,7 @@ RCT_EXPORT_METHOD(completeRemoteNotification: (NSString*) messageId
|
|||
|
||||
// ** Start internals **
|
||||
|
||||
- (NSDictionary*)parseFIRMessagingRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage
|
||||
openedFromTray:(bool)openedFromTray {
|
||||
- (NSDictionary*)parseFIRMessagingRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage {
|
||||
NSDictionary *appData = remoteMessage.appData;
|
||||
|
||||
NSMutableDictionary *message = [[NSMutableDictionary alloc] init];
|
||||
|
@ -262,46 +274,46 @@ RCT_EXPORT_METHOD(completeRemoteNotification: (NSString*) messageId
|
|||
} else if ([k1 isEqualToString:@"from"]) {
|
||||
message[@"from"] = appData[k1];
|
||||
} else if ([k1 isEqualToString:@"notification"]) {
|
||||
NSDictionary *notification = appData[k1];
|
||||
NSMutableDictionary *notif = [[NSMutableDictionary alloc] init];
|
||||
for (id k2 in notification) {
|
||||
if ([k2 isEqualToString:@"badge"]) {
|
||||
notif[@"badge"] = notification[k2];
|
||||
} else if ([k2 isEqualToString:@"body"]) {
|
||||
notif[@"body"] = notification[k2];
|
||||
} else if ([k2 isEqualToString:@"body_loc_args"]) {
|
||||
notif[@"bodyLocalizationArgs"] = notification[k2];
|
||||
} else if ([k2 isEqualToString:@"body_loc_key"]) {
|
||||
notif[@"bodyLocalizationKey"] = notification[k2];
|
||||
} else if ([k2 isEqualToString:@"click_action"]) {
|
||||
notif[@"clickAction"] = notification[k2];
|
||||
} else if ([k2 isEqualToString:@"sound"]) {
|
||||
notif[@"sound"] = notification[k2];
|
||||
} else if ([k2 isEqualToString:@"subtitle"]) {
|
||||
notif[@"subtitle"] = notification[k2];
|
||||
} else if ([k2 isEqualToString:@"title"]) {
|
||||
notif[@"title"] = notification[k2];
|
||||
} else if ([k2 isEqualToString:@"title_loc_args"]) {
|
||||
notif[@"titleLocalizationArgs"] = notification[k2];
|
||||
} else if ([k2 isEqualToString:@"title_loc_key"]) {
|
||||
notif[@"titleLocalizationKey"] = notification[k2];
|
||||
} else {
|
||||
NSLog(@"Unknown notification key: %@", k2);
|
||||
}
|
||||
}
|
||||
message[@"notification"] = notif;
|
||||
// Ignore for messages
|
||||
} else {
|
||||
// Assume custom data key
|
||||
data[k1] = appData[k1];
|
||||
}
|
||||
}
|
||||
message[@"messageType"] = @"RemoteMessage";
|
||||
message[@"data"] = data;
|
||||
message[@"openedFromTray"] = @(false);
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
- (NSDictionary*)parseUserInfo:(NSDictionary *)userInfo {
|
||||
NSMutableDictionary *message = [[NSMutableDictionary alloc] init];
|
||||
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
|
||||
|
||||
for (id k1 in userInfo) {
|
||||
if ([k1 isEqualToString:@"aps"]) {
|
||||
// Ignore notification section
|
||||
} else if ([k1 isEqualToString:@"gcm.message_id"]) {
|
||||
message[@"messageId"] = userInfo[k1];
|
||||
} else if ([k1 isEqualToString:@"google.c.a.ts"]) {
|
||||
message[@"sentTime"] = userInfo[k1];
|
||||
} else if ([k1 isEqualToString:@"gcm.n.e"]
|
||||
|| [k1 isEqualToString:@"gcm.notification.sound2"]
|
||||
|| [k1 isEqualToString:@"google.c.a.c_id"]
|
||||
|| [k1 isEqualToString:@"google.c.a.c_l"]
|
||||
|| [k1 isEqualToString:@"google.c.a.e"]
|
||||
|| [k1 isEqualToString:@"google.c.a.udt"]) {
|
||||
// Ignore known keys
|
||||
} else {
|
||||
// Assume custom data
|
||||
data[k1] = userInfo[k1];
|
||||
}
|
||||
}
|
||||
|
||||
message[@"data"] = data;
|
||||
|
||||
return message;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)supportedEvents {
|
||||
return @[MESSAGING_MESSAGE_RECEIVED, MESSAGING_TOKEN_REFRESHED];
|
||||
}
|
||||
|
@ -317,4 +329,3 @@ RCT_EXPORT_METHOD(completeRemoteNotification: (NSString*) messageId
|
|||
@implementation RNFirebaseMessaging
|
||||
@end
|
||||
#endif
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#if __has_include(<FirebaseMessaging/FIRMessaging.h>)
|
||||
#import "RNFirebaseEvents.h"
|
||||
#import "RNFirebaseMessaging.h"
|
||||
#import "RNFirebaseUtil.h"
|
||||
#import <React/RCTUtils.h>
|
||||
|
||||
|
@ -37,7 +38,7 @@ RCT_EXPORT_MODULE();
|
|||
- (void)configure {
|
||||
// If we're on iOS 10 then we need to set this as a delegate for the UNUserNotificationCenter
|
||||
#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0
|
||||
[UNUserNotificationCenter currentNotificationCenter].delegate = self;
|
||||
// [UNUserNotificationCenter currentNotificationCenter].delegate = self;
|
||||
#endif
|
||||
|
||||
// Set static instance for use from AppDelegate
|
||||
|
@ -50,26 +51,78 @@ RCT_EXPORT_MODULE();
|
|||
// *******************************************************
|
||||
|
||||
- (void)didReceiveLocalNotification:(nonnull UILocalNotification *)notification {
|
||||
|
||||
NSString *event;
|
||||
if (RCTSharedApplication().applicationState == UIApplicationStateBackground) {
|
||||
// notification_displayed
|
||||
event = NOTIFICATIONS_NOTIFICATION_DISPLAYED;
|
||||
} else if (RCTSharedApplication().applicationState == UIApplicationStateInactive) {
|
||||
// notification_displayed
|
||||
event = NOTIFICATIONS_NOTIFICATION_PRESSED;
|
||||
} else {
|
||||
// notification_received
|
||||
event = NOTIFICATIONS_NOTIFICATION_RECEIVED;
|
||||
}
|
||||
|
||||
NSDictionary *message = [self parseUILocalNotification:notification];
|
||||
[RNFirebaseUtil sendJSEvent:self name:event body:message];
|
||||
}
|
||||
|
||||
// Listen for background messages
|
||||
- (void)didReceiveRemoteNotification:(nonnull NSDictionary *)userInfo {
|
||||
BOOL isFromBackground = (RCTSharedApplication().applicationState == UIApplicationStateInactive);
|
||||
NSDictionary *message = [self parseUserInfo:userInfo messageType:@"RemoteNotification" clickAction:nil openedFromTray:isFromBackground];
|
||||
// FCM Data messages come through here if they specify content-available=true
|
||||
// Pass them over to the RNFirebaseMessaging handler instead
|
||||
if (userInfo[@"aps"] && ((NSDictionary*)userInfo[@"aps"]).count == 1 && userInfo[@"aps"][@"content-available"]) {
|
||||
[[RNFirebaseMessaging instance] didReceiveRemoteNotification:userInfo];
|
||||
return;
|
||||
}
|
||||
|
||||
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_MESSAGE_RECEIVED body:message];
|
||||
NSString *event;
|
||||
if (RCTSharedApplication().applicationState == UIApplicationStateBackground) {
|
||||
// notification_displayed
|
||||
event = NOTIFICATIONS_NOTIFICATION_DISPLAYED;
|
||||
} else if (RCTSharedApplication().applicationState == UIApplicationStateInactive) {
|
||||
// notification_displayed
|
||||
event = NOTIFICATIONS_NOTIFICATION_PRESSED;
|
||||
} else {
|
||||
// notification_received
|
||||
event = NOTIFICATIONS_NOTIFICATION_RECEIVED;
|
||||
}
|
||||
|
||||
// TODO: Proper notification structure
|
||||
NSDictionary *message = [self parseUserInfo:userInfo messageType:@"RemoteNotification" clickAction:nil];
|
||||
|
||||
[RNFirebaseUtil sendJSEvent:self name:event body:message];
|
||||
}
|
||||
|
||||
// Listen for background messages
|
||||
- (void)didReceiveRemoteNotification:(NSDictionary *)userInfo
|
||||
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
|
||||
BOOL isFromBackground = (RCTSharedApplication().applicationState == UIApplicationStateInactive);
|
||||
NSDictionary *message = [self parseUserInfo:userInfo messageType:@"RemoteNotificationHandler" clickAction:nil openedFromTray:isFromBackground];
|
||||
// FCM Data messages come through here if they specify content-available=true
|
||||
// Pass them over to the RNFirebaseMessaging handler instead
|
||||
if (userInfo[@"aps"] && ((NSDictionary*)userInfo[@"aps"]).count == 1 && userInfo[@"aps"][@"content-available"]) {
|
||||
[[RNFirebaseMessaging instance] didReceiveRemoteNotification:userInfo];
|
||||
return;
|
||||
}
|
||||
|
||||
NSString *event;
|
||||
if (RCTSharedApplication().applicationState == UIApplicationStateBackground) {
|
||||
// notification_displayed
|
||||
event = NOTIFICATIONS_NOTIFICATION_DISPLAYED;
|
||||
} else if (RCTSharedApplication().applicationState == UIApplicationStateInactive) {
|
||||
// notification_displayed
|
||||
event = NOTIFICATIONS_NOTIFICATION_PRESSED;
|
||||
} else {
|
||||
// notification_received
|
||||
event = NOTIFICATIONS_NOTIFICATION_RECEIVED;
|
||||
}
|
||||
|
||||
// TODO: Proper notification structure
|
||||
NSDictionary *message = [self parseUserInfo:userInfo messageType:@"RemoteNotificationHandler" clickAction:nil];
|
||||
|
||||
// TODO: Callback handler
|
||||
// [_callbackHandlers setObject:[completionHandler copy] forKey:message[@"messageId"]];
|
||||
|
||||
[RNFirebaseUtil sendJSEvent:self name:MESSAGING_MESSAGE_RECEIVED body:message];
|
||||
[RNFirebaseUtil sendJSEvent:self name:event body:message];
|
||||
}
|
||||
|
||||
// *******************************************************
|
||||
|
@ -93,7 +146,7 @@ RCT_EXPORT_MODULE();
|
|||
|
||||
NSString *event;
|
||||
UNNotificationPresentationOptions options;
|
||||
NSDictionary *message = [self parseUNNotification:notification messageType:@"PresentNotification" openedFromTray:false];
|
||||
NSDictionary *message = [self parseUNNotification:notification messageType:@"PresentNotification"];
|
||||
|
||||
if (isFcm || isScheduled) {
|
||||
// If app is in the background
|
||||
|
@ -116,9 +169,7 @@ RCT_EXPORT_MODULE();
|
|||
event = NOTIFICATIONS_NOTIFICATION_DISPLAYED;
|
||||
}
|
||||
|
||||
if (event) {
|
||||
[RNFirebaseUtil sendJSEvent:self name:event body:message];
|
||||
}
|
||||
[RNFirebaseUtil sendJSEvent:self name:event body:message];
|
||||
completionHandler(options);
|
||||
}
|
||||
|
||||
|
@ -130,9 +181,9 @@ didReceiveNotificationResponse:(UNNotificationResponse *)response
|
|||
#else
|
||||
withCompletionHandler:(void(^)())completionHandler {
|
||||
#endif
|
||||
NSDictionary *message = [self parseUNNotification:response.notification messageType:@"NotificationResponse" openedFromTray:true];
|
||||
NSDictionary *message = [self parseUNNotification:response.notification messageType:@"NotificationResponse"];
|
||||
|
||||
[RNFirebaseUtil sendJSEvent:self name:NOTIFICATIONS_NOTIFICATION_CLICKED body:message];
|
||||
[RNFirebaseUtil sendJSEvent:self name:NOTIFICATIONS_NOTIFICATION_PRESSED body:message];
|
||||
completionHandler();
|
||||
}
|
||||
|
||||
|
@ -200,7 +251,13 @@ RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve rejecte
|
|||
NSDictionary *notification = [self parseUILocalNotification:localNotification];
|
||||
resolve(notification);
|
||||
} else {
|
||||
resolve(nil);
|
||||
NSDictionary *remoteNotification = [self bridge].launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey];
|
||||
if (remoteNotification) {
|
||||
NSDictionary *message = [self parseUserInfo:remoteNotification messageType:@"InitialMessage" clickAction:nil];
|
||||
resolve(message);
|
||||
} else {
|
||||
resolve(nil);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -485,7 +542,7 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
|
|||
}
|
||||
ios[@"attachments"] = attachments;
|
||||
}
|
||||
|
||||
|
||||
if (localNotification.content.badge) {
|
||||
ios[@"badge"] = localNotification.content.badge;
|
||||
}
|
||||
|
@ -504,21 +561,20 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
|
|||
}
|
||||
|
||||
- (NSDictionary*)parseUNNotification:(UNNotification *)notification
|
||||
messageType:(NSString *)messageType
|
||||
openedFromTray:(bool)openedFromTray {
|
||||
messageType:(NSString *)messageType {
|
||||
NSDictionary *userInfo = notification.request.content.userInfo;
|
||||
NSString *clickAction = notification.request.content.categoryIdentifier;
|
||||
|
||||
return [self parseUserInfo:userInfo messageType:messageType clickAction:clickAction openedFromTray:openedFromTray];
|
||||
return [self parseUserInfo:userInfo messageType:messageType clickAction:clickAction];
|
||||
}
|
||||
|
||||
- (NSDictionary*)parseUserInfo:(NSDictionary *)userInfo
|
||||
messageType:(NSString *) messageType
|
||||
clickAction:(NSString *) clickAction
|
||||
openedFromTray:(bool)openedFromTray {
|
||||
NSMutableDictionary *message = [[NSMutableDictionary alloc] init];
|
||||
NSMutableDictionary *notif = [[NSMutableDictionary alloc] init];
|
||||
clickAction:(NSString *) clickAction {
|
||||
|
||||
NSMutableDictionary *notification = [[NSMutableDictionary alloc] init];
|
||||
NSMutableDictionary *data = [[NSMutableDictionary alloc] init];
|
||||
NSMutableDictionary *ios = [[NSMutableDictionary alloc] init];
|
||||
|
||||
for (id k1 in userInfo) {
|
||||
if ([k1 isEqualToString:@"aps"]) {
|
||||
|
@ -528,37 +584,42 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
|
|||
NSDictionary *alert = aps[k2];
|
||||
for (id k3 in alert) {
|
||||
if ([k3 isEqualToString:@"body"]) {
|
||||
notif[@"body"] = alert[k3];
|
||||
notification[@"body"] = alert[k3];
|
||||
} else if ([k3 isEqualToString:@"loc-args"]) {
|
||||
notif[@"bodyLocalizationArgs"] = alert[k3];
|
||||
// TODO: What to do with this?
|
||||
// notif[@"bodyLocalizationArgs"] = alert[k3];
|
||||
} else if ([k3 isEqualToString:@"loc-key"]) {
|
||||
notif[@"bodyLocalizationKey"] = alert[k3];
|
||||
// TODO: What to do with this?
|
||||
// notif[@"bodyLocalizationKey"] = alert[k3];
|
||||
} else if ([k3 isEqualToString:@"subtitle"]) {
|
||||
notif[@"subtitle"] = alert[k3];
|
||||
notification[@"subtitle"] = alert[k3];
|
||||
} else if ([k3 isEqualToString:@"title"]) {
|
||||
notif[@"title"] = alert[k3];
|
||||
notification[@"title"] = alert[k3];
|
||||
} else if ([k3 isEqualToString:@"title-loc-args"]) {
|
||||
notif[@"titleLocalizationArgs"] = alert[k3];
|
||||
// TODO: What to do with this?
|
||||
// notif[@"titleLocalizationArgs"] = alert[k3];
|
||||
} else if ([k3 isEqualToString:@"title-loc-key"]) {
|
||||
notif[@"titleLocalizationKey"] = alert[k3];
|
||||
// TODO: What to do with this?
|
||||
// notif[@"titleLocalizationKey"] = alert[k3];
|
||||
} else {
|
||||
NSLog(@"Unknown alert key: %@", k2);
|
||||
}
|
||||
}
|
||||
} else if ([k2 isEqualToString:@"badge"]) {
|
||||
notif[@"badge"] = aps[k2];
|
||||
ios[@"badge"] = aps[k2];
|
||||
} else if ([k2 isEqualToString:@"category"]) {
|
||||
notif[@"clickAction"] = aps[k2];
|
||||
ios[@"category"] = aps[k2];
|
||||
} else if ([k2 isEqualToString:@"sound"]) {
|
||||
notif[@"sound"] = aps[k2];
|
||||
notification[@"sound"] = aps[k2];
|
||||
} else {
|
||||
NSLog(@"Unknown aps key: %@", k2);
|
||||
}
|
||||
}
|
||||
} else if ([k1 isEqualToString:@"gcm.message_id"]) {
|
||||
message[@"messageId"] = userInfo[k1];
|
||||
notification[@"notificationId"] = userInfo[k1];
|
||||
} else if ([k1 isEqualToString:@"google.c.a.ts"]) {
|
||||
message[@"sentTime"] = userInfo[k1];
|
||||
// TODO: What to do with this?
|
||||
// message[@"sentTime"] = userInfo[k1];
|
||||
} else if ([k1 isEqualToString:@"gcm.n.e"]
|
||||
|| [k1 isEqualToString:@"gcm.notification.sound2"]
|
||||
|| [k1 isEqualToString:@"google.c.a.c_id"]
|
||||
|
@ -572,26 +633,17 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
|
|||
}
|
||||
}
|
||||
|
||||
if (!notif[@"clickAction"] && clickAction) {
|
||||
notif[@"clickAction"] = clickAction;
|
||||
}
|
||||
// TODO: What to do with this?
|
||||
// message[@"messageType"] = messageType;
|
||||
|
||||
// Generate a message ID if one was not present in the notification
|
||||
// This is used for resolving click handlers
|
||||
if (!message[@"messageId"]) {
|
||||
message[@"messageId"] = [[NSUUID UUID] UUIDString];
|
||||
}
|
||||
message[@"messageType"] = messageType;
|
||||
notification[@"data"] = data;
|
||||
notification[@"ios"] = ios;
|
||||
|
||||
message[@"data"] = data;
|
||||
message[@"notification"] = notif;
|
||||
message[@"openedFromTray"] = @(openedFromTray);
|
||||
|
||||
return message;
|
||||
return notification;
|
||||
}
|
||||
|
||||
- (NSArray<NSString *> *)supportedEvents {
|
||||
return @[NOTIFICATIONS_NOTIFICATION_CLICKED, NOTIFICATIONS_NOTIFICATION_DISPLAYED, NOTIFICATIONS_NOTIFICATION_RECEIVED];
|
||||
return @[NOTIFICATIONS_NOTIFICATION_DISPLAYED, NOTIFICATIONS_NOTIFICATION_PRESSED, NOTIFICATIONS_NOTIFICATION_RECEIVED];
|
||||
}
|
||||
|
||||
+ (BOOL)requiresMainQueueSetup
|
||||
|
@ -605,4 +657,3 @@ RCT_EXPORT_METHOD(scheduleNotification:(NSDictionary*) notification
|
|||
@implementation RNFirebaseNotifications
|
||||
@end
|
||||
#endif
|
||||
|
||||
|
|
|
@ -11,9 +11,7 @@ import {
|
|||
} from './types';
|
||||
import type Messaging from './';
|
||||
import type {
|
||||
MessageTypeType,
|
||||
NativeMessage,
|
||||
Notification,
|
||||
PresentNotificationResultType,
|
||||
RemoteNotificationResultType,
|
||||
} from './types';
|
||||
|
@ -47,18 +45,6 @@ export default class Message {
|
|||
return this._message.messageId;
|
||||
}
|
||||
|
||||
get messageType(): ?MessageTypeType {
|
||||
return this._message.messageType;
|
||||
}
|
||||
|
||||
get openedFromTray(): boolean {
|
||||
return this._message.openedFromTray;
|
||||
}
|
||||
|
||||
get notification(): ?Notification {
|
||||
return this._message.notification;
|
||||
}
|
||||
|
||||
get sentTime(): ?number {
|
||||
return this._message.sentTime;
|
||||
}
|
||||
|
|
|
@ -55,8 +55,8 @@ export default class Messaging extends ModuleBase {
|
|||
// sub to internal native event - this fans out to
|
||||
// public event name: onMessage
|
||||
'messaging_message_received',
|
||||
(message: Message) => {
|
||||
SharedEventEmitter.emit('onMessage', message);
|
||||
(message: NativeMessage) => {
|
||||
SharedEventEmitter.emit('onMessage', new Message(this, message));
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -89,8 +89,7 @@ export default class Messaging extends ModuleBase {
|
|||
|
||||
getLogger(this).info('Creating onMessage listener');
|
||||
|
||||
const wrappedListener = async (nativeMessage: NativeMessage) => {
|
||||
const message = new Message(this, nativeMessage);
|
||||
const wrappedListener = async (message: Message) => {
|
||||
await listener(message);
|
||||
message.complete();
|
||||
};
|
||||
|
|
|
@ -51,9 +51,6 @@ export type NativeMessage = {
|
|||
data: { [string]: string },
|
||||
from?: string,
|
||||
messageId: string,
|
||||
messageType?: MessageTypeType,
|
||||
openedFromTray: boolean,
|
||||
notification?: Notification,
|
||||
sentTime?: number,
|
||||
to?: string,
|
||||
ttl?: number,
|
||||
|
|
|
@ -2,128 +2,29 @@
|
|||
* @flow
|
||||
* AndroidNotification representation wrapper
|
||||
*/
|
||||
import { Category } from './types';
|
||||
import type Notification from './Notification';
|
||||
|
||||
type Lights = {
|
||||
argb: number,
|
||||
onMs: number,
|
||||
offMs: number,
|
||||
};
|
||||
|
||||
type Progress = {
|
||||
max: number,
|
||||
progress: number,
|
||||
indeterminate: boolean,
|
||||
};
|
||||
|
||||
type SmallIcon = {
|
||||
icon: string,
|
||||
level?: number,
|
||||
};
|
||||
|
||||
export type NativeAndroidNotification = {|
|
||||
// TODO actions: Action[],
|
||||
autoCancel: boolean,
|
||||
badgeIconType: BadgeIconTypeType,
|
||||
category: CategoryType,
|
||||
channelId: string,
|
||||
clickAction?: string,
|
||||
color: string,
|
||||
colorized: boolean,
|
||||
contentInfo: string,
|
||||
defaults: DefaultsType[],
|
||||
group: string,
|
||||
groupAlertBehaviour: GroupAlertType,
|
||||
groupSummary: boolean,
|
||||
largeIcon: string,
|
||||
lights: Lights,
|
||||
localOnly: boolean,
|
||||
number: number,
|
||||
ongoing: boolean,
|
||||
onlyAlertOnce: boolean,
|
||||
people: string[],
|
||||
priority: PriorityType,
|
||||
progress: Progress,
|
||||
// publicVersion: Notification,
|
||||
remoteInputHistory: string[],
|
||||
shortcutId: string,
|
||||
showWhen: boolean,
|
||||
smallIcon: SmallIcon,
|
||||
sortKey: string,
|
||||
// TODO: style: Style,
|
||||
ticker: string,
|
||||
timeoutAfter: number,
|
||||
usesChronometer: boolean,
|
||||
vibrate: number[],
|
||||
visibility: VisibilityType,
|
||||
when: number,
|
||||
|};
|
||||
|
||||
export const BadgeIconType = {
|
||||
Large: 2,
|
||||
None: 0,
|
||||
Small: 1,
|
||||
};
|
||||
|
||||
export const Category = {
|
||||
Alarm: 'alarm',
|
||||
Call: 'call',
|
||||
Email: 'email',
|
||||
Error: 'err',
|
||||
Event: 'event',
|
||||
Message: 'msg',
|
||||
Progress: 'progress',
|
||||
Promo: 'promo',
|
||||
Recommendation: 'recommendation',
|
||||
Reminder: 'reminder',
|
||||
Service: 'service',
|
||||
Social: 'social',
|
||||
Status: 'status',
|
||||
System: 'system',
|
||||
Transport: 'transport',
|
||||
};
|
||||
|
||||
export const Defaults = {
|
||||
All: -1,
|
||||
Lights: 4,
|
||||
Sound: 1,
|
||||
Vibrate: 2,
|
||||
};
|
||||
|
||||
export const GroupAlert = {
|
||||
All: 0,
|
||||
Children: 2,
|
||||
Summary: 1,
|
||||
};
|
||||
|
||||
export const Priority = {
|
||||
Default: 0,
|
||||
High: 1,
|
||||
Low: -1,
|
||||
Max: 2,
|
||||
Min: -2,
|
||||
};
|
||||
|
||||
export const Visibility = {
|
||||
Private: 0,
|
||||
Public: 1,
|
||||
Secret: -1,
|
||||
};
|
||||
|
||||
type BadgeIconTypeType = $Values<typeof BadgeIconType>;
|
||||
type CategoryType = $Values<typeof Category>;
|
||||
type DefaultsType = $Values<typeof Defaults>;
|
||||
type GroupAlertType = $Values<typeof GroupAlert>;
|
||||
type PriorityType = $Values<typeof Priority>;
|
||||
type VisibilityType = $Values<typeof Visibility>;
|
||||
import type {
|
||||
BadgeIconTypeType,
|
||||
CategoryType,
|
||||
DefaultsType,
|
||||
GroupAlertType,
|
||||
Lights,
|
||||
NativeAndroidNotification,
|
||||
PriorityType,
|
||||
Progress,
|
||||
SmallIcon,
|
||||
VisibilityType,
|
||||
} from './types';
|
||||
|
||||
export default class AndroidNotification {
|
||||
// TODO optional fields
|
||||
// TODO actions: Action[]; // icon, title, ??pendingIntent??, allowGeneratedReplies, extender, extras, remoteinput (ugh)
|
||||
_autoCancel: boolean;
|
||||
_badgeIconType: BadgeIconTypeType;
|
||||
_category: CategoryType;
|
||||
_channelId: string;
|
||||
_clickAction: string;
|
||||
_clickAction: string | void;
|
||||
_color: string;
|
||||
_colorized: boolean;
|
||||
_contentInfo: string;
|
||||
|
@ -145,9 +46,7 @@ export default class AndroidNotification {
|
|||
_remoteInputHistory: string[];
|
||||
_shortcutId: string;
|
||||
_showWhen: boolean;
|
||||
_smallIcon: SmallIcon = {
|
||||
icon: 'ic_launcher',
|
||||
};
|
||||
_smallIcon: SmallIcon;
|
||||
_sortKey: string;
|
||||
// TODO: style: Style; // Need to figure out if this can work
|
||||
_ticker: string;
|
||||
|
@ -167,9 +66,50 @@ export default class AndroidNotification {
|
|||
// fullScreenIntent: PendingIntent
|
||||
// sound.streamType
|
||||
|
||||
constructor(notification: Notification) {
|
||||
constructor(notification: Notification, data?: NativeAndroidNotification) {
|
||||
this._notification = notification;
|
||||
this._people = [];
|
||||
|
||||
if (data) {
|
||||
this._autoCancel = data.autoCancel;
|
||||
this._badgeIconType = data.badgeIconType;
|
||||
this._category = data.category;
|
||||
this._channelId = data.channelId;
|
||||
this._clickAction = data.clickAction;
|
||||
this._color = data.color;
|
||||
this._colorized = data.colorized;
|
||||
this._contentInfo = data.contentInfo;
|
||||
this._defaults = data.defaults;
|
||||
this._group = data.group;
|
||||
this._groupAlertBehaviour = data.groupAlertBehaviour;
|
||||
this._groupSummary = data.groupSummary;
|
||||
this._largeIcon = data.largeIcon;
|
||||
this._lights = data.lights;
|
||||
this._localOnly = data.localOnly;
|
||||
this._number = data.number;
|
||||
this._ongoing = data.ongoing;
|
||||
this._onlyAlertOnce = data.onlyAlertOnce;
|
||||
this._people = data.people;
|
||||
this._priority = data.priority;
|
||||
this._progress = data.progress;
|
||||
// _publicVersion: Notification;
|
||||
this._remoteInputHistory = data.remoteInputHistory;
|
||||
this._shortcutId = data.shortcutId;
|
||||
this._showWhen = data.showWhen;
|
||||
this._smallIcon = data.smallIcon;
|
||||
this._sortKey = data.sortKey;
|
||||
this._ticker = data.ticker;
|
||||
this._timeoutAfter = data.timeoutAfter;
|
||||
this._usesChronometer = data.usesChronometer;
|
||||
this._vibrate = data.vibrate;
|
||||
this._visibility = data.visibility;
|
||||
this._when = data.when;
|
||||
}
|
||||
|
||||
// Defaults
|
||||
this._people = this._people || [];
|
||||
this._smallIcon = this._smallIcon || {
|
||||
icon: 'ic_launcher',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -506,7 +446,7 @@ export default class AndroidNotification {
|
|||
}
|
||||
|
||||
build(): NativeAndroidNotification {
|
||||
// TODO: Validation
|
||||
// TODO: Validation of required fields
|
||||
if (!this._channelId) {
|
||||
throw new Error(
|
||||
'AndroidNotification: Missing required `channelId` property'
|
||||
|
|
|
@ -3,48 +3,37 @@
|
|||
* IOSNotification representation wrapper
|
||||
*/
|
||||
import type Notification from './Notification';
|
||||
|
||||
type AttachmentOptions = {|
|
||||
TypeHint: string,
|
||||
ThumbnailHidden: boolean,
|
||||
ThumbnailClippingRect: {
|
||||
height: number,
|
||||
width: number,
|
||||
x: number,
|
||||
y: number,
|
||||
},
|
||||
ThumbnailTime: number,
|
||||
|};
|
||||
|
||||
type Attachment = {|
|
||||
identifier: string,
|
||||
options?: AttachmentOptions,
|
||||
url: string,
|
||||
|};
|
||||
|
||||
export type NativeIOSNotification = {|
|
||||
alertAction?: string,
|
||||
attachments: Attachment[],
|
||||
badge?: number,
|
||||
category?: string,
|
||||
hasAction?: boolean,
|
||||
launchImage?: string,
|
||||
threadIdentifier?: string,
|
||||
|};
|
||||
import type {
|
||||
Attachment,
|
||||
AttachmentOptions,
|
||||
NativeIOSNotification,
|
||||
} from './types';
|
||||
|
||||
export default class IOSNotification {
|
||||
_alertAction: string; // alertAction | N/A
|
||||
_alertAction: string | void; // alertAction | N/A
|
||||
_attachments: Attachment[]; // N/A | attachments
|
||||
_badge: number; // applicationIconBadgeNumber | badge
|
||||
_category: string;
|
||||
_hasAction: boolean; // hasAction | N/A
|
||||
_launchImage: string; // alertLaunchImage | launchImageName
|
||||
_badge: number | void; // applicationIconBadgeNumber | badge
|
||||
_category: string | void;
|
||||
_hasAction: boolean | void; // hasAction | N/A
|
||||
_launchImage: string | void; // alertLaunchImage | launchImageName
|
||||
_notification: Notification;
|
||||
_threadIdentifier: string; // N/A | threadIdentifier
|
||||
_threadIdentifier: string | void; // N/A | threadIdentifier
|
||||
|
||||
constructor(notification: Notification) {
|
||||
this._attachments = [];
|
||||
constructor(notification: Notification, data?: NativeIOSNotification) {
|
||||
this._notification = notification;
|
||||
|
||||
if (data) {
|
||||
this._alertAction = data.alertAction;
|
||||
this._attachments = data.attachments;
|
||||
this._badge = data.badge;
|
||||
this._category = data.category;
|
||||
this._hasAction = data.hasAction;
|
||||
this._launchImage = data.launchImage;
|
||||
this._threadIdentifier = data.threadIdentifier;
|
||||
}
|
||||
|
||||
// Defaults
|
||||
this._attachments = this._attachments || [];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -128,7 +117,7 @@ export default class IOSNotification {
|
|||
}
|
||||
|
||||
build(): NativeIOSNotification {
|
||||
// TODO: Validation
|
||||
// TODO: Validation of required fields
|
||||
|
||||
return {
|
||||
alertAction: this._alertAction,
|
||||
|
|
|
@ -7,21 +7,7 @@ import AndroidNotification from './AndroidNotification';
|
|||
import IOSNotification from './IOSNotification';
|
||||
import { generatePushID, isObject } from '../../utils';
|
||||
|
||||
import type { NativeAndroidNotification } from './AndroidNotification';
|
||||
import type { NativeIOSNotification } from './IOSNotification';
|
||||
import type { Schedule } from './';
|
||||
|
||||
type NativeNotification = {|
|
||||
android?: NativeAndroidNotification,
|
||||
body: string,
|
||||
data: { [string]: string },
|
||||
ios?: NativeIOSNotification,
|
||||
notificationId: string,
|
||||
schedule?: Schedule,
|
||||
sound?: string,
|
||||
subtitle?: string,
|
||||
title: string,
|
||||
|};
|
||||
import type { NativeNotification } from './types';
|
||||
|
||||
export default class Notification {
|
||||
// iOS 8/9 | 10+ | Android
|
||||
|
@ -34,12 +20,23 @@ export default class Notification {
|
|||
_subtitle: string | void; // N/A | subtitle | subText
|
||||
_title: string; // alertTitle | title | contentTitle
|
||||
|
||||
constructor() {
|
||||
this._android = new AndroidNotification(this);
|
||||
this._data = {};
|
||||
this._ios = new IOSNotification(this);
|
||||
// TODO: Is this the best way to generate an ID?
|
||||
this._notificationId = generatePushID();
|
||||
constructor(data?: NativeNotification) {
|
||||
this._android = new AndroidNotification(this, data && data.android);
|
||||
this._ios = new IOSNotification(this, data && data.ios);
|
||||
|
||||
if (data) {
|
||||
this._body = data.body;
|
||||
this._data = data.data;
|
||||
// TODO: Is this the best way to generate an ID?
|
||||
this._notificationId = data.notificationId;
|
||||
this._sound = data.sound;
|
||||
this._subtitle = data.subtitle;
|
||||
this._title = data.title;
|
||||
}
|
||||
|
||||
// Defaults
|
||||
this._data = this._data || {};
|
||||
this._notificationId = this._notificationId || generatePushID();
|
||||
}
|
||||
|
||||
get android(): AndroidNotification {
|
||||
|
|
|
@ -15,26 +15,20 @@ import {
|
|||
GroupAlert,
|
||||
Priority,
|
||||
Visibility,
|
||||
} from './AndroidNotification';
|
||||
} from './types';
|
||||
|
||||
import type App from '../core/app';
|
||||
import type { NativeNotification, Schedule } from './types';
|
||||
|
||||
// TODO: Received notification type will be different from sent notification
|
||||
type OnNotification = Notification => any;
|
||||
|
||||
type OnNotificationObserver = {
|
||||
next: OnNotification,
|
||||
};
|
||||
|
||||
export type Schedule = {
|
||||
exact?: boolean,
|
||||
fireDate: number,
|
||||
repeatInterval?: 'minute' | 'hour' | 'day' | 'week',
|
||||
};
|
||||
|
||||
const NATIVE_EVENTS = [
|
||||
'notifications_notification_clicked',
|
||||
'notifications_notification_displayed',
|
||||
'notifications_notification_pressed',
|
||||
'notifications_notification_received',
|
||||
];
|
||||
|
||||
|
@ -69,10 +63,13 @@ export default class Notifications extends ModuleBase {
|
|||
|
||||
SharedEventEmitter.addListener(
|
||||
// sub to internal native event - this fans out to
|
||||
// public event name: onNotificationClicked
|
||||
'notifications_notification_clicked',
|
||||
(notification: Notification) => {
|
||||
SharedEventEmitter.emit('onNotificationClicked', notification);
|
||||
// public event name: onNotificationPressed
|
||||
'notifications_notification_pressed',
|
||||
(notification: NativeNotification) => {
|
||||
SharedEventEmitter.emit(
|
||||
'onNotificationPressed',
|
||||
new Notification(notification)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -80,8 +77,11 @@ export default class Notifications extends ModuleBase {
|
|||
// sub to internal native event - this fans out to
|
||||
// public event name: onNotificationDisplayed
|
||||
'notifications_notification_displayed',
|
||||
(notification: Notification) => {
|
||||
SharedEventEmitter.emit('onNotificationDisplayed', notification);
|
||||
(notification: NativeNotification) => {
|
||||
SharedEventEmitter.emit(
|
||||
'onNotificationDisplayed',
|
||||
new Notification(notification)
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -89,8 +89,11 @@ export default class Notifications extends ModuleBase {
|
|||
// sub to internal native event - this fans out to
|
||||
// public event name: onNotification
|
||||
'notifications_notification_received',
|
||||
(notification: Notification) => {
|
||||
SharedEventEmitter.emit('onNotification', notification);
|
||||
(notification: NativeNotification) => {
|
||||
SharedEventEmitter.emit(
|
||||
'onNotification',
|
||||
new Notification(notification)
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
@ -166,29 +169,6 @@ export default class Notifications extends ModuleBase {
|
|||
};
|
||||
}
|
||||
|
||||
onNotificationClicked(
|
||||
nextOrObserver: OnNotification | OnNotificationObserver
|
||||
): () => any {
|
||||
let listener;
|
||||
if (isFunction(nextOrObserver)) {
|
||||
listener = nextOrObserver;
|
||||
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
|
||||
listener = nextOrObserver.next;
|
||||
} else {
|
||||
throw new Error(
|
||||
'Notifications.onNotificationClicked failed: First argument must be a function or observer object with a `next` function.'
|
||||
);
|
||||
}
|
||||
|
||||
getLogger(this).info('Creating onNotificationClicked listener');
|
||||
SharedEventEmitter.addListener('onNotificationClicked', listener);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onNotificationClicked listener');
|
||||
SharedEventEmitter.removeListener('onNotificationClicked', listener);
|
||||
};
|
||||
}
|
||||
|
||||
onNotificationDisplayed(
|
||||
nextOrObserver: OnNotification | OnNotificationObserver
|
||||
): () => any {
|
||||
|
@ -212,6 +192,30 @@ export default class Notifications extends ModuleBase {
|
|||
};
|
||||
}
|
||||
|
||||
onNotificationPressed(
|
||||
nextOrObserver: OnNotification | OnNotificationObserver
|
||||
): () => any {
|
||||
let listener: Notification => any;
|
||||
if (isFunction(nextOrObserver)) {
|
||||
// $FlowBug: Not coping with the overloaded method signature
|
||||
listener = nextOrObserver;
|
||||
} else if (isObject(nextOrObserver) && isFunction(nextOrObserver.next)) {
|
||||
listener = nextOrObserver.next;
|
||||
} else {
|
||||
throw new Error(
|
||||
'Notifications.onNotificationPressed failed: First argument must be a function or observer object with a `next` function.'
|
||||
);
|
||||
}
|
||||
|
||||
getLogger(this).info('Creating onNotificationPressed listener');
|
||||
SharedEventEmitter.addListener('onNotificationPressed', listener);
|
||||
|
||||
return () => {
|
||||
getLogger(this).info('Removing onNotificationPressed listener');
|
||||
SharedEventEmitter.removeListener('onNotificationPressed', listener);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all delivered notifications.
|
||||
* @returns {*}
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
export const BadgeIconType = {
|
||||
Large: 2,
|
||||
None: 0,
|
||||
Small: 1,
|
||||
};
|
||||
|
||||
export const Category = {
|
||||
Alarm: 'alarm',
|
||||
Call: 'call',
|
||||
Email: 'email',
|
||||
Error: 'err',
|
||||
Event: 'event',
|
||||
Message: 'msg',
|
||||
Progress: 'progress',
|
||||
Promo: 'promo',
|
||||
Recommendation: 'recommendation',
|
||||
Reminder: 'reminder',
|
||||
Service: 'service',
|
||||
Social: 'social',
|
||||
Status: 'status',
|
||||
System: 'system',
|
||||
Transport: 'transport',
|
||||
};
|
||||
|
||||
export const Defaults = {
|
||||
All: -1,
|
||||
Lights: 4,
|
||||
Sound: 1,
|
||||
Vibrate: 2,
|
||||
};
|
||||
|
||||
export const GroupAlert = {
|
||||
All: 0,
|
||||
Children: 2,
|
||||
Summary: 1,
|
||||
};
|
||||
|
||||
export const Priority = {
|
||||
Default: 0,
|
||||
High: 1,
|
||||
Low: -1,
|
||||
Max: 2,
|
||||
Min: -2,
|
||||
};
|
||||
|
||||
export const Visibility = {
|
||||
Private: 0,
|
||||
Public: 1,
|
||||
Secret: -1,
|
||||
};
|
||||
|
||||
export type BadgeIconTypeType = $Values<typeof BadgeIconType>;
|
||||
export type CategoryType = $Values<typeof Category>;
|
||||
export type DefaultsType = $Values<typeof Defaults>;
|
||||
export type GroupAlertType = $Values<typeof GroupAlert>;
|
||||
export type PriorityType = $Values<typeof Priority>;
|
||||
export type VisibilityType = $Values<typeof Visibility>;
|
||||
|
||||
export type Lights = {
|
||||
argb: number,
|
||||
onMs: number,
|
||||
offMs: number,
|
||||
};
|
||||
|
||||
export type Progress = {
|
||||
max: number,
|
||||
progress: number,
|
||||
indeterminate: boolean,
|
||||
};
|
||||
|
||||
export type SmallIcon = {
|
||||
icon: string,
|
||||
level?: number,
|
||||
};
|
||||
|
||||
export type NativeAndroidNotification = {|
|
||||
// TODO actions: Action[],
|
||||
autoCancel: boolean,
|
||||
badgeIconType: BadgeIconTypeType,
|
||||
category: CategoryType,
|
||||
channelId: string,
|
||||
clickAction?: string,
|
||||
color: string,
|
||||
colorized: boolean,
|
||||
contentInfo: string,
|
||||
defaults: DefaultsType[],
|
||||
group: string,
|
||||
groupAlertBehaviour: GroupAlertType,
|
||||
groupSummary: boolean,
|
||||
largeIcon: string,
|
||||
lights: Lights,
|
||||
localOnly: boolean,
|
||||
number: number,
|
||||
ongoing: boolean,
|
||||
onlyAlertOnce: boolean,
|
||||
people: string[],
|
||||
priority: PriorityType,
|
||||
progress: Progress,
|
||||
// publicVersion: Notification,
|
||||
remoteInputHistory: string[],
|
||||
shortcutId: string,
|
||||
showWhen: boolean,
|
||||
smallIcon: SmallIcon,
|
||||
sortKey: string,
|
||||
// TODO: style: Style,
|
||||
ticker: string,
|
||||
timeoutAfter: number,
|
||||
usesChronometer: boolean,
|
||||
vibrate: number[],
|
||||
visibility: VisibilityType,
|
||||
when: number,
|
||||
|};
|
||||
|
||||
export type AttachmentOptions = {|
|
||||
TypeHint: string,
|
||||
ThumbnailHidden: boolean,
|
||||
ThumbnailClippingRect: {
|
||||
height: number,
|
||||
width: number,
|
||||
x: number,
|
||||
y: number,
|
||||
},
|
||||
ThumbnailTime: number,
|
||||
|};
|
||||
|
||||
export type Attachment = {|
|
||||
identifier: string,
|
||||
options?: AttachmentOptions,
|
||||
url: string,
|
||||
|};
|
||||
|
||||
export type NativeIOSNotification = {|
|
||||
alertAction?: string,
|
||||
attachments: Attachment[],
|
||||
badge?: number,
|
||||
category?: string,
|
||||
hasAction?: boolean,
|
||||
launchImage?: string,
|
||||
threadIdentifier?: string,
|
||||
|};
|
||||
|
||||
export type Schedule = {
|
||||
exact?: boolean,
|
||||
fireDate: number,
|
||||
repeatInterval?: 'minute' | 'hour' | 'day' | 'week',
|
||||
};
|
||||
|
||||
export type NativeNotification = {|
|
||||
android?: NativeAndroidNotification,
|
||||
body: string,
|
||||
data: { [string]: string },
|
||||
ios?: NativeIOSNotification,
|
||||
notificationId: string,
|
||||
schedule?: Schedule,
|
||||
sound?: string,
|
||||
subtitle?: string,
|
||||
title: string,
|
||||
|};
|
Loading…
Reference in New Issue