Skip to content

Commit

Permalink
chore(Revert): "fix: move push prompt logic to typescript, fix token …
Browse files Browse the repository at this point in the history
…registration bugs" (#8992)

Revert "fix: move push prompt logic to typescript, fix token registration bugs (#8862)"

This reverts commit 0b45f9a.
  • Loading branch information
gkartalis authored Jul 13, 2023
1 parent a5ea2fc commit 7f7cb1e
Show file tree
Hide file tree
Showing 33 changed files with 334 additions and 669 deletions.
1 change: 0 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<uses-feature android:name="android.hardware.camera" android:required="false" />
<uses-feature android:name="android.hardware.camera.front" android:required="false" />
Expand Down
6 changes: 3 additions & 3 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ buildscript {
ext {
googlePlayServicesVersion = "17.0.0"
firebaseMessagingVersion = "21.1.0" // matching firebaseIidVersion to avoid duplicate class error
buildToolsVersion = "33.0.0"
buildToolsVersion = "31.0.0"
minSdkVersion = 21
compileSdkVersion = 33
targetSdkVersion = 33
compileSdkVersion = 31
targetSdkVersion = 31
firebaseIidVersion = "21.1.0" // Needed for react-native-device-info
googlePlayServicesAuthVersion = "16.0.1"
kotlinVersion = "1.5.31"
Expand Down
2 changes: 2 additions & 0 deletions ios/Artsy/App/ARAnalyticsConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@ extern NSString *const ARAnalyticsAppUsageCountProperty;

// Notifications

extern NSString *const ARAnalyticsEnabledNotificationsProperty;
extern NSString *const ARAnalyticsNotificationReceived;
extern NSString *const ARAnalyticsNotificationTapped;
extern NSString *const ARAnalyticsPushNotificationsRequested;

// Push notifications

Expand Down
2 changes: 2 additions & 0 deletions ios/Artsy/App/ARAnalyticsConstants.m
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

NSString *const ARAnalyticsAppUsageCountProperty = @"app launched count";

NSString *const ARAnalyticsEnabledNotificationsProperty = @"has enabled notifications";
NSString *const ARAnalyticsNotificationReceived = @"notification received";
NSString *const ARAnalyticsNotificationTapped = @"notification tapped";
NSString *const ARAnalyticsPushNotificationsRequested = @"push notifications requested";

NSString *const ARAnalyticsPushNotificationLocal = @"Artsy notification prompt response";
NSString *const ARAnalyticsPushNotificationApple = @"Apple notification prompt response";
Expand Down
12 changes: 12 additions & 0 deletions ios/Artsy/App/ARAppDelegate+Emission.m
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,18 @@ - (AREmission *)setupSharedEmission

[AREmission setSharedInstance:emission];

#pragma mark - Native Module: Push Notification Permissions

emission.APIModule.directNotificationPermissionPrompter = ^() {
ARAppNotificationsDelegate *delegate = [[JSDecoupledAppDelegate sharedAppDelegate] remoteNotificationsDelegate];
[delegate registerForDeviceNotificationsWithApple];
};

emission.APIModule.prepromptNotificationPermissionPrompter = ^() {
ARAppNotificationsDelegate *delegate = [[JSDecoupledAppDelegate sharedAppDelegate] remoteNotificationsDelegate];
[delegate registerForDeviceNotificationsWithContext:ARAppNotificationsRequestContextOnboarding];
};

#pragma mark - Native Module: Follow status

emission.APIModule.notificationReadStatusAssigner = ^(RCTResponseSenderBlock block) {
Expand Down
4 changes: 4 additions & 0 deletions ios/Artsy/App/ARAppNotificationsDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ typedef NS_ENUM(NSInteger, ARAppNotificationsRequestContext) {

@property (nonatomic, readwrite, assign) ARAppNotificationsRequestContext requestContext;

- (void)registerForDeviceNotificationsWithContext:(ARAppNotificationsRequestContext)requestContext;
- (void)applicationDidReceiveRemoteNotification:(NSDictionary *)userInfo inApplicationState:(UIApplicationState)applicationState;

/// Used in admin tools and for react native to request permissions
- (void)registerForDeviceNotificationsWithApple;

@end
155 changes: 154 additions & 1 deletion ios/Artsy/App/ARAppNotificationsDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,157 @@

@implementation ARAppNotificationsDelegate

#pragma mark -
#pragma mark Local Push Notification Alerts

- (void)registerForDeviceNotificationsWithContext:(ARAppNotificationsRequestContext)requestContext
{
self.requestContext = requestContext;

if (![AROptions boolForOption:ARPushNotificationsSettingsPromptSeen] &&
[AROptions boolForOption:ARPushNotificationsAppleDialogueRejected]) {
// if you've rejected Apple's push notification and you've not seen our prompt to send you to settings
// lets show you a prompt to go to settings
[self displayPushNotificationSettingsPrompt];
} else if (![AROptions boolForOption:ARPushNotificationsAppleDialogueSeen] && [self shouldPresentPushNotificationAgain]) {
// As long as you've not seen Apple's dialogue already we will show you our pre-prompt.
[self displayPushNotificationLocalRequestPrompt];
} else {
// Otherwise fallback to requesting directly with apple to make sure we have
// up to date push tokens
[self registerForDeviceNotificationsWithApple];
}
}

- (void)displayPushNotificationLocalRequestPrompt
{
UIAlertController *alert = [self pushNotificationPromptAlertController];

UIAlertAction *confirmAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[self registerUserInterest];
}];
UIAlertAction *cancelAction = [UIAlertAction actionWithTitle:@"Don't Allow" style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[self registerUserDisinterest];
}];
[alert addAction:cancelAction];
[alert addAction:confirmAction];

[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alert animated:YES completion:nil];
}


- (void)displayPushNotificationSettingsPrompt
{
UIAlertController *alert = [self pushNotificationPromptAlertController];

UIAlertAction *settingsAction = [UIAlertAction actionWithTitle:@"Go to Settings" style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
[self presentSettings];
}];
[alert addAction:settingsAction];
alert.preferredAction = settingsAction;
[alert addAction:[UIAlertAction actionWithTitle:@"No thanks" style:UIAlertActionStyleCancel handler:nil]];
[[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alert animated:YES completion:nil];

[AROptions setBool:YES forOption:ARPushNotificationsSettingsPromptSeen];
}

- (void)registerUserInterest
{
NSString *analyticsContext = @"";
if (self.requestContext == ARAppNotificationsRequestContextArtistFollow) {
analyticsContext = @"ArtistFollow";
} else if (self.requestContext == ARAppNotificationsRequestContextOnboarding) {
analyticsContext = @"Onboarding";
} else if (self.requestContext == ARAppNotificationsRequestContextLaunch) {
analyticsContext = @"Launch";
}

analyticsContext = [@[@"PushNotification", analyticsContext] componentsJoinedByString:@""];

[[AREmission sharedInstance] sendEvent:ARAnalyticsPushNotificationLocal traits:@{
@"action_type" : @"Tap",
@"action_name" : @"Yes",
@"context_screen" : analyticsContext,
}];
[self registerForDeviceNotificationsWithApple];
}

- (void)registerUserDisinterest
{
// Well, in that case we'll store today's date
// and prompt the user in a week's time, if they perform certain actions (e.g. follow an artist)

NSString *analyticsContext = @"";
if (self.requestContext == ARAppNotificationsRequestContextArtistFollow) {
analyticsContext = @"ArtistFollow";
} else if (self.requestContext == ARAppNotificationsRequestContextOnboarding) {
analyticsContext = @"Onboarding";
} else if (self.requestContext == ARAppNotificationsRequestContextLaunch) {
analyticsContext = @"Launch";
}

analyticsContext = [@[@"PushNotification", analyticsContext] componentsJoinedByString:@""];

[[AREmission sharedInstance] sendEvent:ARAnalyticsPushNotificationLocal traits:@{
@"action_type" : @"Tap",
@"action_name" : @"Cancel",
@"context_screen" : analyticsContext
}];
[[NSUserDefaults standardUserDefaults] setObject:[NSDate date] forKey:ARPushNotificationsDialogueLastSeenDate];
}

- (void)presentSettings
{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:UIApplicationOpenSettingsURLString] options:@{} completionHandler:nil];
}

- (UIAlertController *)pushNotificationPromptAlertController
{
return [UIAlertController alertControllerWithTitle:@"Artsy Would Like to Send You Notifications"
message:@"Turn on notifications to get important updates about artists you follow."
preferredStyle:UIAlertControllerStyleAlert];
}

- (BOOL)shouldPresentPushNotificationAgain
{
// we don't want to ask too often
// currently, we make sure at least a week has passed by since you last saw the dialogue

NSDate *lastSeenPushNotification = [[NSUserDefaults standardUserDefaults] objectForKey:ARPushNotificationsDialogueLastSeenDate];

if (lastSeenPushNotification) {
NSDate *currentDate = [NSDate date];

NSTimeInterval timePassed = [currentDate timeIntervalSinceDate:lastSeenPushNotification];
NSTimeInterval weekInSeconds = (60 * 60 * 24 * 7);

return timePassed >= weekInSeconds;
} else {
// if you've never seen one before, we'll show you ;)
return YES;
}
}

#pragma mark -
#pragma mark Push Notification Register

- (void)registerForDeviceNotificationsWithApple
{
ARActionLog(@"Registering with Apple for remote notifications.");
UNAuthorizationOptions authOptions = (UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert);
[[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError * _Nullable error) {
NSString *grantedString = granted ? @"YES" : @"NO";
[[AREmission sharedInstance] sendEvent:ARAnalyticsPushNotificationsRequested traits:@{@"granted" : grantedString}];
[[Appboy sharedInstance] pushAuthorizationFromUserNotificationCenter:granted];
}];

[[UIApplication sharedApplication] registerForRemoteNotifications];
[AROptions setBool:YES forOption:ARPushNotificationsAppleDialogueSeen];
}

#pragma mark -
#pragma mark Push Notification Delegate

Expand All @@ -45,6 +196,7 @@ - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotif
}];
#if (TARGET_IPHONE_SIMULATOR == 0)
ARErrorLog(@"Error registering for remote notifications: %@", error.localizedDescription);
[AROptions setBool:YES forOption:ARPushNotificationsAppleDialogueRejected];
#endif
}

Expand Down Expand Up @@ -76,9 +228,10 @@ - (void)application:(UIApplication *)application didRegisterForRemoteNotificatio
ARActionLog(@"Got device notification token: %@", deviceToken);
NSString *previousToken = [[NSUserDefaults standardUserDefaults] stringForKey:ARAPNSDeviceTokenKey];

// Save device token for dev settings and to prevent excess calls to gravity if tokens don't change
// Save device token purely for the dev settings view.
[[NSUserDefaults standardUserDefaults] setValue:deviceToken forKey:ARAPNSDeviceTokenKey];

[[AREmission sharedInstance] sendIdentifyEvent:@{ARAnalyticsEnabledNotificationsProperty: @1}];
[[Appboy sharedInstance] registerDeviceToken:deviceTokenData];

// We only record device tokens on the Artsy service in case of Beta or App Store builds.
Expand Down
9 changes: 9 additions & 0 deletions ios/Artsy/Constants/ARDefaults.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,15 @@ extern NSString *const AROAuthTokenExpiryDateDefault;
extern NSString *const ARXAppTokenKeychainKey;
extern NSString *const ARXAppTokenExpiryDateDefault;

#pragma mark -
#pragma mark push notifications

extern NSString *const ARPushNotificationsAppleDialogueSeen;
extern NSString *const ARPushNotificationsAppleDialogueRejected;
extern NSString *const ARPushNotificationsSettingsPromptSeen;
extern NSString *const ARPushNotificationFollowArtist;
extern NSString *const ARPushNotificationsDialogueLastSeenDate;

#pragma mark -
#pragma mark user permissions

Expand Down
16 changes: 16 additions & 0 deletions ios/Artsy/Constants/ARDefaults.m
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@
NSString *const ARXAppTokenKeychainKey = @"ARXAppTokenDefault";
NSString *const ARXAppTokenExpiryDateDefault = @"ARXAppTokenExpiryDateDefault";

NSString *const ARPushNotificationsAppleDialogueSeen = @"eigen-push-seen-dialogue";
NSString *const ARPushNotificationsAppleDialogueRejected = @"eigen-push-reject-dialogue";
NSString *const ARPushNotificationsSettingsPromptSeen = @"eigen-push-seen-settings-dialogue";
NSString *const ARPushNotificationFollowArtist = @"eigen-push-followed-artist";
NSString *const ARPushNotificationsDialogueLastSeenDate = @"eigen-push-seen-dialogue-date";

NSString *const ARAugmentedRealityHasSeenSetup = @"ARAugmentedRealityHasSeenSetup";
NSString *const ARAugmentedRealityHasTriedToSetup = @"ARAugmentedRealityHasTriedToSetup";
NSString *const ARAugmentedRealityCameraAccessGiven = @"ARAugmentedRealityCameraAccessGiven";
Expand All @@ -25,8 +31,18 @@ + (void)resetDefaults
// Need to save launch count for analytics
NSInteger launchCount = [[NSUserDefaults standardUserDefaults] integerForKey:ARAnalyticsAppUsageCountProperty];

// Preserve notification related settings
BOOL hasSeenNotificationPrompt = [[NSUserDefaults standardUserDefaults] boolForKey:ARPushNotificationsSettingsPromptSeen];
BOOL hasSeenNotificationDialogue = [[NSUserDefaults standardUserDefaults] boolForKey:ARPushNotificationsAppleDialogueSeen];
BOOL userPushNotificationDecision = [[NSUserDefaults standardUserDefaults] boolForKey:ARPushNotificationsAppleDialogueRejected];

[[NSUserDefaults standardUserDefaults] removePersistentDomainForName:[[NSBundle mainBundle] bundleIdentifier]];

[[NSUserDefaults standardUserDefaults] setInteger:launchCount forKey:ARAnalyticsAppUsageCountProperty];
[[NSUserDefaults standardUserDefaults] setBool:hasSeenNotificationPrompt forKey:ARPushNotificationsSettingsPromptSeen];
[[NSUserDefaults standardUserDefaults] setBool:hasSeenNotificationDialogue forKey:ARPushNotificationsAppleDialogueSeen];
[[NSUserDefaults standardUserDefaults] setBool:userPushNotificationDecision forKey:ARPushNotificationsAppleDialogueRejected];

[[NSUserDefaults standardUserDefaults] synchronize];
}
@end
17 changes: 17 additions & 0 deletions ios/Artsy/Emission/TemporaryAPI/ARTemporaryAPIModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,25 @@

typedef void(^ARNotificationReadStatusAssigner)(RCTResponseSenderBlock block);

typedef void(^ARNotificationPermissionsPrompter)();

typedef void(^ARRelativeURLResolver)(NSString *path, RCTPromiseResolveBlock resolve, RCTPromiseRejectBlock reject);


/// While metaphysics is read-only, we need to rely on Eigen's
/// v1 API access to get/set these bits of information.

@interface ARTemporaryAPIModule : NSObject <RCTBridgeModule>


// Just shows the apple dialog, used for explicitly asking permission in settings
@property (nonatomic, copy, readwrite) ARNotificationPermissionsPrompter directNotificationPermissionPrompter;

// Uses some logic to pre-prompt, redirect to settings, and eventually prompt with apple dialog, used on login
@property (nonatomic, copy, readwrite) ARNotificationPermissionsPrompter prepromptNotificationPermissionPrompter;

@property (nonatomic, copy, readwrite) ARNotificationReadStatusAssigner notificationReadStatusAssigner;

@property (nonatomic, copy, readwrite) ARRelativeURLResolver urlResolver;

@end
17 changes: 14 additions & 3 deletions ios/Artsy/Emission/TemporaryAPI/ARTemporaryAPIModule.m
Original file line number Diff line number Diff line change
@@ -1,16 +1,27 @@
#import "ARTemporaryAPIModule.h"
#import <UserNotifications/UserNotifications.h>
#import "AREmission.h"
#import <Appboy.h>


@implementation ARTemporaryAPIModule

RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(markUserPermissionStatus:(BOOL)granted)
RCT_EXPORT_METHOD(requestDirectNotificationPermissions)
{
[[Appboy sharedInstance] pushAuthorizationFromUserNotificationCenter:granted];
/* Used in settings screen to directly ask user for push permissions */
dispatch_async(dispatch_get_main_queue(), ^{
self.directNotificationPermissionPrompter();
});
}


RCT_EXPORT_METHOD(requestPrepromptNotificationPermissions)
{
/* Used on login with some additional logic before requesting permissions */
dispatch_async(dispatch_get_main_queue(), ^{
self.prepromptNotificationPermissionPrompter();
});
}

RCT_EXPORT_METHOD(fetchNotificationPermissions:(RCTResponseSenderBlock)callback)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,13 @@ - (ARCellData *)generateNotificationTokenPasteboardCopy;
[[UIPasteboard generalPasteboard] setValue:deviceToken forPasteboardType:(NSString *)kUTTypePlainText];
}];
}

- (ARCellData *)requestNotificationsAlert;
{
return [self tappableCellDataWithTitle:@"Request Receiving Notifications" selection:^{
[[[ARAppNotificationsDelegate alloc] init] registerForDeviceNotificationsWithApple];
}];
}
#endif


Expand Down
Loading

0 comments on commit 7f7cb1e

Please sign in to comment.