Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App notification settings link #740

Merged
merged 5 commits into from
Jun 2, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion example/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import {
StyleSheet,
View,
Text,
Button
Button,
Platform
} from 'react-native';
import React, {Component} from 'react';
import {Notifications, NotificationAction, NotificationCategory} from 'react-native-notifications';
Expand Down Expand Up @@ -44,6 +45,14 @@ class NotificationsExampleApp extends Component {

completion();
});

Notifications.ios.events().appNotificationSettingsLinked(() => {
console.warn('App Notification Settings Linked')
});
}

requestPermissionsIos(options) {
Notifications.ios.requestPermissions(options);
}

requestPermissions() {
Expand Down Expand Up @@ -134,6 +143,7 @@ class NotificationsExampleApp extends Component {
return (
<View style={styles.container}>
<Button title={'Request permissions'} onPress={this.requestPermissions} testID={'requestPermissions'} />
{Platform.OS === 'ios' && <Button title={'Request permissions with app notification settings'} onPress={() => this.requestPermissionsIos(['ProvidesAppNotificationSettings'])} testID={'requestPermissionsWithAppSettings'} />}
<Button title={'Send local notification'} onPress={this.sendLocalNotification} testID={'sendLocalNotification'} />
<Button title={'Remove all delivered notifications'} onPress={this.removeAllDeliveredNotifications} />
{notifications}
Expand Down
6 changes: 3 additions & 3 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ PODS:
- React-cxxreact (= 0.62.2)
- React-jsi (= 0.62.2)
- React-jsinspector (0.62.2)
- react-native-notifications (3.2.2):
- React
- react-native-notifications (3.4.2):
- React-Core
- React-RCTActionSheet (0.62.2):
- React-Core/RCTActionSheetHeaders (= 0.62.2)
- React-RCTAnimation (0.62.2):
Expand Down Expand Up @@ -354,7 +354,7 @@ SPEC CHECKSUMS:
React-jsi: b6dc94a6a12ff98e8877287a0b7620d365201161
React-jsiexecutor: 1540d1c01bb493ae3124ed83351b1b6a155db7da
React-jsinspector: 512e560d0e985d0e8c479a54a4e5c147a9c83493
react-native-notifications: 4b5d764d8c7a58442ca3ead11f47c75110da8fe4
react-native-notifications: 51ed8167f70f01c5000ba81a4465ea98b4612e23
React-RCTActionSheet: f41ea8a811aac770e0cc6e0ad6b270c644ea8b7c
React-RCTAnimation: 49ab98b1c1ff4445148b72a3d61554138565bad0
React-RCTBlob: a332773f0ebc413a0ce85942a55b064471587a71
Expand Down
4 changes: 2 additions & 2 deletions lib/ios/RNBridgeModule.m
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ - (dispatch_queue_t)methodQueue {

#pragma mark - JS interface

RCT_EXPORT_METHOD(requestPermissions) {
[_commandsHandler requestPermissions];
RCT_EXPORT_METHOD(requestPermissions:(NSArray *)options) {
[_commandsHandler requestPermissions:options];
}

RCT_EXPORT_METHOD(setCategories:(NSArray *)categories) {
Expand Down
2 changes: 1 addition & 1 deletion lib/ios/RNCommandsHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

- (instancetype)init;

- (void)requestPermissions;
- (void)requestPermissions:(NSArray *)options;

- (void)setCategories:(NSArray *)categories;

Expand Down
4 changes: 2 additions & 2 deletions lib/ios/RNCommandsHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ - (instancetype)init {
return self;
}

- (void)requestPermissions {
[_notificationCenter requestPermissions];
- (void)requestPermissions:(NSArray *)options {
[_notificationCenter requestPermissions:options];
}

- (void)setCategories:(NSArray *)categories {
Expand Down
1 change: 1 addition & 0 deletions lib/ios/RNEventEmitter.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ static NSString* const RNNotificationReceived = @"notificationReceived";
static NSString* const RNNotificationReceivedBackground = @"notificationReceivedBackground";
static NSString* const RNNotificationOpened = @"notificationOpened";
static NSString* const RNPushKitNotificationReceived = @"pushKitNotificationReceived";
static NSString* const RNAppNotificationSettingsLinked = @"appNotificationSettingsLinked";


@interface RNEventEmitter : RCTEventEmitter <RCTBridgeModule>
Expand Down
3 changes: 2 additions & 1 deletion lib/ios/RNEventEmitter.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ @implementation RNEventEmitter
RNNotificationReceived,
RNNotificationReceivedBackground,
RNNotificationOpened,
RNPushKitNotificationReceived];
RNPushKitNotificationReceived,
RNAppNotificationSettingsLinked];
}

- (instancetype)init {
Expand Down
2 changes: 1 addition & 1 deletion lib/ios/RNNotificationCenter.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ typedef void (^RCTPromiseRejectBlock)(NSString *code, NSString *message, NSError

- (void)isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve;

- (void)requestPermissions;
- (void)requestPermissions:(NSArray *)options;

- (void)setCategories:(NSArray *)json;

Expand Down
11 changes: 9 additions & 2 deletions lib/ios/RNNotificationCenter.m
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,16 @@

@implementation RNNotificationCenter

- (void)requestPermissions {
- (void)requestPermissions:(NSArray *)options {
UNAuthorizationOptions authOptions = (UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert);
if ([options count] > 0) {
for (NSString* option in options) {
if ([option isEqualToString:@"ProvidesAppNotificationSettings"]) {
authOptions = (UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionProvidesAppNotificationSettings);
}
}
}

[UNUserNotificationCenter.currentNotificationCenter requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (!error && granted) {
[UNUserNotificationCenter.currentNotificationCenter getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
Expand Down Expand Up @@ -85,5 +93,4 @@ - (void)checkPermissions:(RCTPromiseResolveBlock)resolve {
});
}];
}

@end
4 changes: 4 additions & 0 deletions lib/ios/RNNotificationCenterMulticast.m
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#import "RNNotificationCenterMulticast.h"
#import "RNEventEmitter.h"

@implementation RNNotificationCenterMulticast {
NSHashTable *delegates;
Expand Down Expand Up @@ -80,6 +81,9 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center openSettingsFo
}
}
}
dispatch_async(dispatch_get_main_queue(), ^{
[RNEventEmitter sendEvent:RNAppNotificationSettingsLinked body:@{}];
});
}

@end
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,20 @@ - (void)testRequestPermissions_userAuthorizedPermissions {
[[_notificationCenter expect] getNotificationSettingsWithCompletionHandler:[OCMArg invokeBlockWithArgs:settings, nil]];
[[(id)[UIApplication sharedApplication] expect] registerForRemoteNotifications];

[_uut requestPermissions];
[_uut requestPermissions:nil];
[_notificationCenter verify];
}

- (void)testRequestPermissions_withProvidesAppNotificationSettings {
UNAuthorizationOptions authOptions = (UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionProvidesAppNotificationSettings);
UNNotificationSettings* settings = [UNNotificationSettings new];
[settings setValue:@(UNAuthorizationStatusAuthorized) forKey:@"authorizationStatus"];

[[_notificationCenter expect] requestAuthorizationWithOptions:authOptions completionHandler:[OCMArg invokeBlockWithArgs:@(YES), [NSNull null], nil]];
[[_notificationCenter expect] getNotificationSettingsWithCompletionHandler:[OCMArg invokeBlockWithArgs:settings, nil]];
[[(id)[UIApplication sharedApplication] expect] registerForRemoteNotifications];

[_uut requestPermissions:@[@"ProvidesAppNotificationSettings"]];
[_notificationCenter verify];
}

Expand All @@ -47,7 +60,7 @@ - (void)testRequestPermissions_userDeniedPermissions {
[[_notificationCenter expect] getNotificationSettingsWithCompletionHandler:[OCMArg invokeBlockWithArgs:settings, nil]];
[[(id)[UIApplication sharedApplication] reject] registerForRemoteNotifications];

[_uut requestPermissions];
[_uut requestPermissions:nil];
[_notificationCenter verify];
}

Expand Down
6 changes: 3 additions & 3 deletions lib/src/Notifications.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { NativeCommandsSender } from './adapters/NativeCommandsSender';
import { NativeCommandsSender, RequestPermissionsOptions } from './adapters/NativeCommandsSender';
import { NativeEventsReceiver } from './adapters/NativeEventsReceiver';
import { Commands } from './commands/Commands';
import { EventsRegistry } from './events/EventsRegistry';
Expand Down Expand Up @@ -46,8 +46,8 @@ export class NotificationsRoot {
/**
* registerRemoteNotifications
*/
public registerRemoteNotifications() {
this.ios.registerRemoteNotifications();
public registerRemoteNotifications(options?: RequestPermissionsOptions[]) {
this.ios.requestPermissions(options);
this.android.registerRemoteNotifications();
}

Expand Down
5 changes: 3 additions & 2 deletions lib/src/NotificationsIOS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Notification } from './DTO/Notification';
import { Commands } from './commands/Commands';
import { Platform } from 'react-native';
import { EventsRegistryIOS } from './events/EventsRegistryIOS';
import { RequestPermissionsOptions } from './adapters/NativeCommandsSender';

export class NotificationsIOS {
constructor(private readonly commands: Commands, private readonly eventsRegistry: EventsRegistryIOS) {
Expand All @@ -19,8 +20,8 @@ export class NotificationsIOS {
/**
* Request permissions to send remote notifications
*/
public registerRemoteNotifications() {
return this.commands.requestPermissions();
public requestPermissions(options?: RequestPermissionsOptions[]) {
return this.commands.requestPermissions(options);
}

/**
Expand Down
8 changes: 5 additions & 3 deletions lib/src/adapters/NativeCommandsSender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { NotificationChannel } from '../interfaces/NotificationChannel';
interface NativeCommandsModule {
getInitialNotification(): Promise<Object>;
postLocalNotification(notification: Notification, id: number): void;
requestPermissions(): void;
requestPermissions(options?: RequestPermissionsOptions[]): void;
abandonPermissions(): void;
refreshToken(): void;
registerPushKit(): void;
Expand All @@ -28,6 +28,8 @@ interface NativeCommandsModule {
finishHandlingBackgroundAction(notificationId: string, backgroundFetchResult: string): void;
}

export type RequestPermissionsOptions = 'ProvidesAppNotificationSettings';

export class NativeCommandsSender {
private readonly nativeCommandsModule: NativeCommandsModule;
constructor() {
Expand All @@ -42,8 +44,8 @@ export class NativeCommandsSender {
return this.nativeCommandsModule.getInitialNotification();
}

requestPermissions() {
return this.nativeCommandsModule.requestPermissions();
requestPermissions(options?: RequestPermissionsOptions[]) {
return this.nativeCommandsModule.requestPermissions(options);
}

abandonPermissions() {
Expand Down
4 changes: 4 additions & 0 deletions lib/src/adapters/NativeEventsReceiver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ export class NativeEventsReceiver {
return this.emitter.addListener('remoteNotificationsRegistered', callback);
}

public appNotificationSettingsLinked(callback: () => void): EmitterSubscription {
return this.emitter.addListener('appNotificationSettingsLinked', callback);
}

public registerPushKitRegistered(callback: (event: RegisteredPushKit) => void): EmitterSubscription {
return this.emitter.addListener('pushKitRegistered', callback);
}
Expand Down
6 changes: 3 additions & 3 deletions lib/src/commands/Commands.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as _ from 'lodash';
import { NativeCommandsSender } from '../adapters/NativeCommandsSender';
import { NativeCommandsSender, RequestPermissionsOptions } from '../adapters/NativeCommandsSender';
import { Notification } from '../DTO/Notification';
import { NotificationCategory } from '../interfaces/NotificationCategory';
import { NotificationChannel } from '../interfaces/NotificationChannel';
Expand Down Expand Up @@ -30,8 +30,8 @@ export class Commands {
});
}

public requestPermissions() {
const result = this.nativeCommandsSender.requestPermissions();
public requestPermissions(options?: RequestPermissionsOptions[]) {
const result = this.nativeCommandsSender.requestPermissions(options);
return result;
}

Expand Down
7 changes: 7 additions & 0 deletions lib/src/events/EventsRegistryIOS.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,11 @@ describe('EventsRegistryIOS', () => {

call(expectedNotification);
});

it('delegates appNotificationSettingsLinked to nativeEventsReceiver', () => {
const cb = jest.fn();
uut.appNotificationSettingsLinked(cb);
expect(mockNativeEventsReceiver.appNotificationSettingsLinked).toHaveBeenCalledTimes(1);
expect(mockNativeEventsReceiver.appNotificationSettingsLinked).toHaveBeenCalledWith(cb);
});
});
4 changes: 4 additions & 0 deletions lib/src/events/EventsRegistryIOS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,8 @@ export class EventsRegistryIOS {
public registerPushKitNotificationReceived(callback: (event: object, completion: () => void) => void): EmitterSubscription {
return this.nativeEventsReceiver.registerPushKitNotificationReceived(this.completionCallbackWrapper.wrapOpenedCallback(callback));
}

public appNotificationSettingsLinked(callback: () => void): EmitterSubscription {
return this.nativeEventsReceiver.appNotificationSettingsLinked(callback);
}
}
4 changes: 2 additions & 2 deletions website/docs/api/ios-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ title: iOS Specific Commands
sidebar_label: iOS specific
---

## requestPermissions()
## requestPermissions(options?: string[])
Requests notification permissions from iOS, prompting the user's dialog box.

```js
Notifications.ios.requestPermissions();
Notifications.ios.requestPermissions(['ProvidesAppNotificationSettings']);
```

## checkPermissions()
Expand Down
10 changes: 10 additions & 0 deletions website/docs/api/ios-events.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,13 @@ Notifications.ios.events().registerPushKitNotificationReceived((payload: object)
});
```

## appNotificationSettingsLinked()
Fired when the link to app notification settings was clicked.

```js
Notifications.ios.events().appNotificationSettingsLinked(() => {
console.log('App Notification Settings Linked');
// navigate to settings
});
```