diff --git a/plugin.xml b/plugin.xml index 5aeb3d5f8..78d1e5d31 100755 --- a/plugin.xml +++ b/plugin.xml @@ -117,8 +117,11 @@ + + + diff --git a/src/ios/PushPlugin.m b/src/ios/PushPlugin.m index 31078b36d..119e809a8 100644 --- a/src/ios/PushPlugin.m +++ b/src/ios/PushPlugin.m @@ -24,6 +24,7 @@ */ #import "PushPlugin.h" +#import "PushPluginSettings.h" #import "AppDelegate+notification.h" @import Firebase; @@ -121,9 +122,10 @@ - (void)unsubscribe:(CDVInvokedUrlCommand *)command { - (void)init:(CDVInvokedUrlCommand *)command { NSMutableDictionary* options = [command.arguments objectAtIndex:0]; - NSMutableDictionary* iosOptions = [options objectForKey:@"ios"]; - id voipArg = [iosOptions objectForKey:@"voip"]; - if (([voipArg isKindOfClass:[NSString class]] && [voipArg isEqualToString:@"true"]) || [voipArg boolValue]) { + [[PushPluginSettings sharedInstance] updateSettingsWithOptions:[options objectForKey:@"ios"]]; + PushPluginSettings *settings = [PushPluginSettings sharedInstance]; + + if ([settings voipEnabled]) { [self.commandDelegate runInBackground:^ { NSLog(@"[PushPlugin] VoIP set to true"); @@ -142,114 +144,34 @@ - (void)init:(CDVInvokedUrlCommand *)command { [self.commandDelegate runInBackground:^ { NSLog(@"[PushPlugin] register called"); self.callbackId = command.callbackId; - - NSArray* topics = [iosOptions objectForKey:@"topics"]; - [self setFcmTopics:topics]; + self.isInline = NO; + self.fcmTopics = [settings fcmTopics]; + self.forceShow = [settings forceShowEnabled]; + self.clearBadge = [settings clearBadgeEnabled]; + if (self.clearBadge) { + [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; + } UNAuthorizationOptions authorizationOptions = UNAuthorizationOptionNone; - - id badgeArg = [iosOptions objectForKey:@"badge"]; - id soundArg = [iosOptions objectForKey:@"sound"]; - id alertArg = [iosOptions objectForKey:@"alert"]; - id criticalArg = [iosOptions objectForKey:@"critical"]; - id clearBadgeArg = [iosOptions objectForKey:@"clearBadge"]; - id forceShowArg = [iosOptions objectForKey:@"forceShow"]; - - if (([badgeArg isKindOfClass:[NSString class]] && [badgeArg isEqualToString:@"true"]) || [badgeArg boolValue]) - { + if ([settings badgeEnabled]) { authorizationOptions |= UNAuthorizationOptionBadge; } - - if (([soundArg isKindOfClass:[NSString class]] && [soundArg isEqualToString:@"true"]) || [soundArg boolValue]) - { + if ([settings soundEnabled]) { authorizationOptions |= UNAuthorizationOptionSound; } - - if (([alertArg isKindOfClass:[NSString class]] && [alertArg isEqualToString:@"true"]) || [alertArg boolValue]) - { + if ([settings alertEnabled]) { authorizationOptions |= UNAuthorizationOptionAlert; } - if (@available(iOS 12.0, *)) { - if ((([criticalArg isKindOfClass:[NSString class]] && [criticalArg isEqualToString:@"true"]) || [criticalArg boolValue])) - { + if ([settings criticalEnabled]) { authorizationOptions |= UNAuthorizationOptionCriticalAlert; } } - - if (clearBadgeArg == nil || ([clearBadgeArg isKindOfClass:[NSString class]] && [clearBadgeArg isEqualToString:@"false"]) || ![clearBadgeArg boolValue]) { - NSLog(@"[PushPlugin] register: setting badge to false"); - clearBadge = NO; - } else { - NSLog(@"[PushPlugin] register: setting badge to true"); - clearBadge = YES; - [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; - } - NSLog(@"[PushPlugin] register: clear badge is set to %d", clearBadge); - - if (forceShowArg == nil || ([forceShowArg isKindOfClass:[NSString class]] && [forceShowArg isEqualToString:@"false"]) || ![forceShowArg boolValue]) { - NSLog(@"[PushPlugin] register: setting forceShow to false"); - forceShow = NO; - } else { - NSLog(@"[PushPlugin] register: setting forceShow to true"); - forceShow = YES; - } - - isInline = NO; - - NSLog(@"[PushPlugin] register: better button setup"); - // setup action buttons - NSMutableSet *categories = [[NSMutableSet alloc] init]; - id categoryOptions = [iosOptions objectForKey:@"categories"]; - if (categoryOptions != nil && [categoryOptions isKindOfClass:[NSDictionary class]]) { - for (id key in categoryOptions) { - NSLog(@"[PushPlugin] categories: key %@", key); - id category = [categoryOptions objectForKey:key]; - - id yesButton = [category objectForKey:@"yes"]; - UNNotificationAction *yesAction; - if (yesButton != nil && [yesButton isKindOfClass:[NSDictionary class]]) { - yesAction = [self createAction: yesButton]; - } - id noButton = [category objectForKey:@"no"]; - UNNotificationAction *noAction; - if (noButton != nil && [noButton isKindOfClass:[NSDictionary class]]) { - noAction = [self createAction: noButton]; - } - id maybeButton = [category objectForKey:@"maybe"]; - UNNotificationAction *maybeAction; - if (maybeButton != nil && [maybeButton isKindOfClass:[NSDictionary class]]) { - maybeAction = [self createAction: maybeButton]; - } - - // Identifier to include in your push payload and local notification - NSString *identifier = key; - - NSMutableArray *actions = [[NSMutableArray alloc] init]; - if (yesButton != nil) { - [actions addObject:yesAction]; - } - if (noButton != nil) { - [actions addObject:noAction]; - } - if (maybeButton != nil) { - [actions addObject:maybeAction]; - } - - UNNotificationCategory *notificationCategory = [UNNotificationCategory categoryWithIdentifier:identifier - actions:actions - intentIdentifiers:@[] - options:UNNotificationCategoryOptionNone]; - - NSLog(@"[PushPlugin] Adding category %@", key); - [categories addObject:notificationCategory]; - } - } + [self handleNotificationSettingsWithAuthorizationOptions:[NSNumber numberWithInteger:authorizationOptions]]; UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; - [center setNotificationCategories:categories]; - [self handleNotificationSettingsWithAuthorizationOptions:[NSNumber numberWithInteger:authorizationOptions]]; + [center setNotificationCategories:[settings categories]]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleNotificationSettings:) @@ -291,23 +213,6 @@ - (void)init:(CDVInvokedUrlCommand *)command { } } -- (UNNotificationAction *)createAction:(NSDictionary *)dictionary { - NSString *identifier = [dictionary objectForKey:@"callback"]; - NSString *title = [dictionary objectForKey:@"title"]; - UNNotificationActionOptions options = UNNotificationActionOptionNone; - - id mode = [dictionary objectForKey:@"foreground"]; - if (mode != nil && (([mode isKindOfClass:[NSString class]] && [mode isEqualToString:@"true"]) || [mode boolValue])) { - options |= UNNotificationActionOptionForeground; - } - id destructive = [dictionary objectForKey:@"destructive"]; - if (destructive != nil && (([destructive isKindOfClass:[NSString class]] && [destructive isEqualToString:@"true"]) || [destructive boolValue])) { - options |= UNNotificationActionOptionDestructive; - } - - return [UNNotificationAction actionWithIdentifier:identifier title:title options:options]; -} - - (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { if (self.callbackId == nil) { NSLog(@"[PushPlugin] Unexpected call to didRegisterForRemoteNotificationsWithDeviceToken, ignoring: %@", deviceToken); diff --git a/src/ios/PushPluginSettings.h b/src/ios/PushPluginSettings.h new file mode 100644 index 000000000..0ac12cff1 --- /dev/null +++ b/src/ios/PushPluginSettings.h @@ -0,0 +1,28 @@ +// +// PushPluginSettings.h +// cordovaTest +// +// Created by Erisu on 2024/09/14. +// + +#import +#import + +@interface PushPluginSettings : NSObject + +@property (nonatomic, readonly) BOOL badgeEnabled; +@property (nonatomic, readonly) BOOL soundEnabled; +@property (nonatomic, readonly) BOOL alertEnabled; +@property (nonatomic, readonly) BOOL criticalEnabled; +@property (nonatomic, readonly) BOOL clearBadgeEnabled; +@property (nonatomic, readonly) BOOL forceShowEnabled; +@property (nonatomic, readonly) BOOL voipEnabled; + +@property (nonatomic, readonly, strong) NSArray *fcmTopics; +@property (nonatomic, readonly, strong) NSSet *categories; + ++ (instancetype)sharedInstance; + +- (void)updateSettingsWithOptions:(NSDictionary *)options; + +@end diff --git a/src/ios/PushPluginSettings.m b/src/ios/PushPluginSettings.m new file mode 100644 index 000000000..e6ece77ed --- /dev/null +++ b/src/ios/PushPluginSettings.m @@ -0,0 +1,179 @@ +// +// PushPluginSettings.m +// cordovaTest +// +// Created by Erisu on 2024/09/14. +// + +#import "PushPluginSettings.h" + +@interface PushPluginSettings () + +@property (nonatomic, strong) NSMutableDictionary *settingsDictionary; + +@end + +@implementation PushPluginSettings + ++ (instancetype)sharedInstance { + static PushPluginSettings *sharedInstance = nil; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + sharedInstance = [[self alloc] initWithDefaults]; + }); + return sharedInstance; +} + +- (instancetype)initWithDefaults { + self = [super init]; + if (self) { + self.settingsDictionary = [@{ + @"badge" : @(NO), + @"sound" : @(NO), + @"alert" : @(NO), + @"critical" : @(NO), + @"clearBadge" : @(NO), + @"forceShow" : @(NO), + @"voip" : @(NO), + @"fcmTopics" : @[], + @"categories" : [NSSet set] + } mutableCopy]; + } + return self; +} + +- (void)updateSettingsWithOptions:(NSDictionary *)options { + for (NSString *key in options) { + if ([self.settingsDictionary objectForKey:key]) { + // Overrides the default setting if defined and apply the correct formatting based on the key. + if ([key isEqualToString:@"fcmTopics"]) { + self.settingsDictionary[key] = [self parseArrayOption:key fromOptions:options withDefault:nil]; + } else if ([key isEqualToString:@"categories"]) { + self.settingsDictionary[key] = [self parseCategoriesFromOptions:options[key]]; + } else { + self.settingsDictionary[key] = @([self parseOption:key fromOptions:options withDefault:NO]); + } + } else { + NSLog(@"[PushPlugin] Settings: Invalid option key: %@", key); + } + } + + NSLog(@"[PushPlugin] Settings: %@", self.settingsDictionary); +} + +- (BOOL)parseOption:(NSString *)key fromOptions:(NSDictionary *)options withDefault:(BOOL)defaultValue { + id option = [options objectForKey:key]; + if ([option isKindOfClass:[NSString class]]) { + return [option isEqualToString:@"true"]; + } + if ([option respondsToSelector:@selector(boolValue)]) { + return [option boolValue]; + } + return defaultValue; +} + +- (NSArray *)parseArrayOption:(NSString *)key fromOptions:(NSDictionary *)options withDefault:(NSArray *)defaultValue { + id option = [options objectForKey:key]; + if ([option isKindOfClass:[NSArray class]]) { + return (NSArray *)option; + } + return defaultValue; +} + +- (NSSet *)parseCategoriesFromOptions:(NSDictionary *)categoryOptions { + NSMutableSet *categoriesSet = [[NSMutableSet alloc] init]; + if (categoryOptions != nil && [categoryOptions isKindOfClass:[NSDictionary class]]) { + for (id key in categoryOptions) { + NSDictionary *category = [categoryOptions objectForKey:key]; + UNNotificationCategory *notificationCategory = [self createCategoryFromDictionary:category withIdentifier:key]; + if (notificationCategory) { + [categoriesSet addObject:notificationCategory]; + } + } + } + return categoriesSet; +} + +- (UNNotificationCategory *)createCategoryFromDictionary:(NSDictionary *)category withIdentifier:(NSString *)identifier { + NSMutableArray *actions = [[NSMutableArray alloc] init]; + + UNNotificationAction *yesAction = [self createActionFromDictionary:[category objectForKey:@"yes"]]; + if (yesAction) + [actions addObject:yesAction]; + + UNNotificationAction *noAction = [self createActionFromDictionary:[category objectForKey:@"no"]]; + if (noAction) + [actions addObject:noAction]; + + UNNotificationAction *maybeAction = [self createActionFromDictionary:[category objectForKey:@"maybe"]]; + if (maybeAction) + [actions addObject:maybeAction]; + + return [UNNotificationCategory categoryWithIdentifier:identifier actions:actions intentIdentifiers:@[] options:UNNotificationCategoryOptionNone]; +} + +- (UNNotificationAction *)createActionFromDictionary:(NSDictionary *)dictionary { + if (![dictionary isKindOfClass:[NSDictionary class]]) { + return nil; + } + + NSString *identifier = [dictionary objectForKey:@"identifier"]; + NSString *title = [dictionary objectForKey:@"title"]; + + if (!title || !identifier) { + return nil; + } + + UNNotificationActionOptions options = UNNotificationActionOptionNone; + id foreground = [dictionary objectForKey:@"foreground"]; + if (foreground != nil && (([foreground isKindOfClass:[NSString class]] && [foreground isEqualToString:@"true"]) || [foreground boolValue])) { + options |= UNNotificationActionOptionForeground; + } + + id destructive = [dictionary objectForKey:@"destructive"]; + if (destructive != nil && (([destructive isKindOfClass:[NSString class]] && [destructive isEqualToString:@"true"]) || [destructive boolValue])) { + options |= UNNotificationActionOptionDestructive; + } + + return [UNNotificationAction actionWithIdentifier:identifier title:title options:options]; +} + +#pragma mark - Getters for individual settings + +- (BOOL)badgeEnabled { + return [self.settingsDictionary[@"badge"] boolValue]; +} + +- (BOOL)soundEnabled { + return [self.settingsDictionary[@"sound"] boolValue]; +} + +- (BOOL)alertEnabled { + return [self.settingsDictionary[@"alert"] boolValue]; +} + +- (BOOL)criticalEnabled { + return [self.settingsDictionary[@"critical"] boolValue]; +} + +- (BOOL)clearBadgeEnabled { + return [self.settingsDictionary[@"clearBadge"] boolValue]; +} + +- (BOOL)forceShowEnabled { + return [self.settingsDictionary[@"forceShow"] boolValue]; +} + +- (BOOL)voipEnabled { + return [self.settingsDictionary[@"voip"] boolValue]; +} + +- (NSArray *)fcmTopics { + return self.settingsDictionary[@"fcmTopics"]; +} + +- (NSSet *)categories { + return self.settingsDictionary[@"categories"]; +} + +@end