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

Some edit reverted notification cleanup #4017

Merged
merged 4 commits into from Sep 14, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -42,7 +42,6 @@ import CocoaLumberjackSwift
}

final class RemoteNotificationsModelController: NSObject {
public static let modelDidChangeNotification = NSNotification.Name(rawValue: "RemoteNotificationsModelDidChange")
public static let didLoadPersistentStoresNotification = NSNotification.Name(rawValue: "ModelControllerDidLoadPersistentStores")

let managedObjectContext: NSManagedObjectContext
Expand Down Expand Up @@ -90,7 +89,6 @@ final class RemoteNotificationsModelController: NSObject {
managedObjectContext = NSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = container.persistentStoreCoordinator
super.init()
NotificationCenter.default.addObserver(self, selector: #selector(managedObjectContextDidSave(_:)), name: NSNotification.Name.NSManagedObjectContextDidSave, object: managedObjectContext)
}

deinit {
Expand Down Expand Up @@ -136,26 +134,6 @@ final class RemoteNotificationsModelController: NSObject {
}
}

// MARK: Validation

let validNotificationCategories: Set<RemoteNotificationCategory> = [.editReverted]

private func validateCategory(of notification: RemoteNotificationsAPIController.NotificationsResult.Notification) -> Bool {
guard let categoryString = notification.category else {
assertionFailure("Missing notification category")
return false
}
let category = RemoteNotificationCategory(stringValue: categoryString)
return validNotificationCategories.contains(category)
}

private func validateAge(ofNotificationDated date: Date) -> Bool {
let sinceNow = Int(date.timeIntervalSinceNow)
let hoursPassed = abs(sinceNow / 3600)
let maxHoursPassed = 24
return hoursPassed <= maxHoursPassed
}

public func createNewNotifications(from notificationsFetchedFromTheServer: Set<RemoteNotificationsAPIController.NotificationsResult.Notification>, completion: @escaping () -> Void) throws {
managedObjectContext.perform {
for notification in notificationsFetchedFromTheServer {
Expand All @@ -178,12 +156,6 @@ final class RemoteNotificationsModelController: NSObject {
assertionFailure("Notification must have an id")
return
}
guard self.validateCategory(of: notification) else {
return
}
guard self.validateAge(ofNotificationDated: date) else {
return
}
let message = notification.message?.header?.wmf_stringByRemovingHTML()
let _ = managedObjectContext.wmf_create(entityNamed: "RemoteNotification",
withKeysAndValues: ["id": id,
Expand Down Expand Up @@ -285,37 +257,4 @@ final class RemoteNotificationsModelController: NSObject {
self.save()
}
}

// MARK: Notifications

@objc private func managedObjectContextDidSave(_ note: Notification) {
guard let userInfo = note.userInfo else {
assertionFailure("Expected note with userInfo dictionary")
return
}
if let insertedObjects = userInfo[NSInsertedObjectsKey] as? Set<NSManagedObject>, !insertedObjects.isEmpty {
postModelDidChangeNotification(ofType: .addedNewNotifications, withNotificationsFromObjects: insertedObjects)
}
if let updatedObjects = userInfo[NSUpdatedObjectsKey] as? Set<NSManagedObject>, !updatedObjects.isEmpty {
postModelDidChangeNotification(ofType: .updatedExistingNotifications, withNotificationsFromObjects: updatedObjects)
}
}

private func postModelDidChangeNotification(ofType modelChangeType: RemoteNotificationsModelChangeType, withNotificationsFromObjects objects: Set<NSManagedObject>) {
let notifications = objects.compactMap { $0 as? RemoteNotification }.filter { $0.state == nil }
guard !notifications.isEmpty else {
return
}
let notificationsGroupedByCategoryNumber = Dictionary(grouping: notifications, by: { NSNumber(value: $0.category.rawValue) })
let modelChange = RemoteNotificationsModelChange(type: modelChangeType, notificationsGroupedByCategoryNumber: notificationsGroupedByCategoryNumber)
let responseCoordinator = RemoteNotificationsModelChangeResponseCoordinator(modelChange: modelChange, modelController: self)
DispatchQueue.main.async {
NotificationCenter.default.post(name: RemoteNotificationsModelController.modelDidChangeNotification, object: responseCoordinator)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apologies if I'm missing its usage in the subsequent PRs, but would it also make sense to remove line 45 which no longer appears to be used?

public static let modelDidChangeNotification = NSNotification.Name(rawValue: "RemoteNotificationsModelDidChange")

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch! I pushed a commit removing this.

}
}
}


@objc public class RemoteNotificationsModelControllerNotification: NSObject {
@objc public static let modelDidChange = RemoteNotificationsModelController.modelDidChangeNotification
}
71 changes: 0 additions & 71 deletions Wikipedia/Code/WMFAppViewController.m
Expand Up @@ -206,11 +206,6 @@ - (void)viewDidLoad {
name:WMFExploreFeedPreferencesDidChangeNotification
object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(remoteNotificationsModelDidChange:)
name:[RemoteNotificationsModelControllerNotification modelDidChange]
object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(userWasLoggedOut:)
name:[WMFAuthenticationManager didLogOutNotification]
Expand Down Expand Up @@ -1624,12 +1619,6 @@ - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecogni
// The method will be called on the delegate only if the application is in the foreground. If the method is not implemented or the handler is not called in a timely manner then the notification will not be presented. The application can choose to have the notification presented as a sound, badge, alert and/or in the notification list. This decision should be based on whether the information in the notification is otherwise visible to the user.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
completionHandler(UNNotificationPresentationOptionAlert);
UNNotificationContent *notificationContent = notification.request.content;
NSString *categoryIdentifier = notificationContent.categoryIdentifier;
NSString *notificationID = (NSString *)notificationContent.userInfo[WMFEditRevertedNotificationIDKey];
if ([categoryIdentifier isEqualToString:WMFEditRevertedNotificationCategoryIdentifier]) {
[self.remoteNotificationsModelChangeResponseCoordinator markAsSeenNotificationWithID:notificationID];
}
}

// The method will be called on the delegate when the user responded to the notification by opening the application, dismissing the notification or choosing a UNNotificationAction. The delegate must be set before the application returns from applicationDidFinishLaunching:.
Expand All @@ -1648,23 +1637,6 @@ - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNoti
[self showInTheNewsForNotificationInfo:info];
} else if ([actionIdentifier isEqualToString:UNNotificationDismissActionIdentifier]) {
}
// WMFEditRevertedNotification
} else if ([categoryIdentifier isEqualToString:WMFEditRevertedNotificationCategoryIdentifier]) {
NSDictionary *info = response.notification.request.content.userInfo;
NSString *articleURLString = (NSString *)info[WMFNotificationInfoArticleURLStringKey];
NSString *notificationID = (NSString *)info[WMFEditRevertedNotificationIDKey];
assert(articleURLString);
assert(notificationID);
if ([actionIdentifier isEqualToString:WMFEditRevertedReadMoreActionIdentifier] || [actionIdentifier isEqualToString:UNNotificationDefaultActionIdentifier]) {
NSURL *articleURL = [NSURL URLWithString:articleURLString];
assert(articleURL);
[self showReadMoreAboutRevertedEditViewControllerWithArticleURL:articleURL
completion:^{
[self.remoteNotificationsModelChangeResponseCoordinator markAsReadNotificationWithID:notificationID];
}];
} else {
[self.remoteNotificationsModelChangeResponseCoordinator markAsReadNotificationWithID:notificationID];
}
}

completionHandler();
Expand Down Expand Up @@ -2025,49 +1997,6 @@ - (void)setRemoteNotificationRegistrationStatusWithDeviceToken: (nullable NSData
[self.notificationsController setRemoteNotificationRegistrationStatusWithDeviceToken:deviceToken error:error];
}

- (void)remoteNotificationsModelDidChange:(NSNotification *)note {
self.remoteNotificationsModelChangeResponseCoordinator = (RemoteNotificationsModelChangeResponseCoordinator *)note.object;
assert(self.remoteNotificationsModelChangeResponseCoordinator);
RemoteNotificationsModelChange *modelChange = (RemoteNotificationsModelChange *)self.remoteNotificationsModelChangeResponseCoordinator.modelChange;
NSDictionary<NSNumber *, NSArray<RemoteNotification *> *> *notificationsGroupedByCategoryNumber = (NSDictionary<NSNumber *, NSArray<RemoteNotification *> *> *)modelChange.notificationsGroupedByCategoryNumber;
assert(modelChange);
switch (modelChange.type) {
case RemoteNotificationsModelChangeTypeAddedNewNotifications:
case RemoteNotificationsModelChangeTypeUpdatedExistingNotifications:
[self scheduleLocalNotificationsForRemoteNotificationsWithCategory:RemoteNotificationCategoryEditReverted notificationsGroupedByCategoryNumber:notificationsGroupedByCategoryNumber];
default:
break;
}
}

- (void)scheduleLocalNotificationsForRemoteNotificationsWithCategory:(RemoteNotificationCategory)category notificationsGroupedByCategoryNumber:(NSDictionary<NSNumber *, NSArray<RemoteNotification *> *> *)notificationsGroupedByCategoryNumber {
NSAssert(category == RemoteNotificationCategoryEditReverted, @"Categories other than RemoteNotificationCategoryEditReverted are not supported");

NSNumber *editRevertedCategoryNumber = [NSNumber numberWithInt:RemoteNotificationCategoryEditReverted];
NSArray<RemoteNotification *> *editRevertedNotifications = notificationsGroupedByCategoryNumber[editRevertedCategoryNumber];
if (editRevertedNotifications.count == 0) {
return;
}

for (RemoteNotification *editRevertedNotification in editRevertedNotifications) {
WMFArticle *article = [self.dataStore fetchArticleWithWikidataID:editRevertedNotification.affectedPageID];
if (!article || !article.displayTitle || !article.URL || !editRevertedNotification.agent) {
// Exclude notifications without article context or notification agent
[self.remoteNotificationsModelChangeResponseCoordinator markAsExcluded:editRevertedNotification];
} else {
NSString *articleTitle = article.displayTitle;
NSString *agent = editRevertedNotification.agent;

NSString *notificationTitle = [WMFCommonStrings revertedEditTitle];
NSString *notificationBodyFormat = WMFLocalizedStringWithDefaultValue(@"reverted-edit-notification-body", nil, nil, @"The edit you made of the article %1$@ was reverted by %2$@", @"Title for notification telling user that the edit they made was reverted. %1$@ is replaced with the title of the affected article, %2$@ is replaced with the username of the person who reverted the edit.");
NSString *notificationBody = [NSString localizedStringWithFormat:notificationBodyFormat, articleTitle, agent];

NSDictionary *userInfo = @{WMFNotificationInfoArticleURLStringKey: article.URL.absoluteString, WMFEditRevertedNotificationIDKey: editRevertedNotification.id};
[self.notificationsController sendNotificationWithTitle:notificationTitle body:notificationBody categoryIdentifier:WMFEditRevertedNotificationCategoryIdentifier userInfo:userInfo atDateComponents:nil];
}
}
}

- (void)showReadMoreAboutRevertedEditViewControllerWithArticleURL:(NSURL *)articleURL completion:(void (^)(void))completion {
ReadMoreAboutRevertedEditViewController *readMoreViewController = [[ReadMoreAboutRevertedEditViewController alloc] initWithNibName:@"ReadMoreAboutRevertedEditViewController" bundle:nil];
readMoreViewController.delegate = self;
Expand Down
4 changes: 0 additions & 4 deletions Wikipedia/Code/WMFNotificationsController.h
Expand Up @@ -8,10 +8,6 @@ extern NSString *const WMFInTheNewsNotificationReadNowActionIdentifier;
extern NSString *const WMFInTheNewsNotificationSaveForLaterActionIdentifier;
extern NSString *const WMFInTheNewsNotificationShareActionIdentifier;

extern NSString *const WMFEditRevertedNotificationCategoryIdentifier;
extern NSString *const WMFEditRevertedReadMoreActionIdentifier;
extern NSString *const WMFEditRevertedNotificationIDKey;

extern NSString *const WMFNotificationInfoArticleTitleKey;
extern NSString *const WMFNotificationInfoArticleURLStringKey;
extern NSString *const WMFNotificationInfoThumbnailURLStringKey;
Expand Down
11 changes: 2 additions & 9 deletions Wikipedia/Code/WMFNotificationsController.m
Expand Up @@ -9,10 +9,6 @@
NSString *const WMFInTheNewsNotificationSaveForLaterActionIdentifier = @"inTheNewsNotificationSaveForLaterActionIdentifier";
NSString *const WMFInTheNewsNotificationShareActionIdentifier = @"inTheNewsNotificationShareActionIdentifier";

NSString *const WMFEditRevertedNotificationCategoryIdentifier = @"WMFEditRevertedNotificationCategoryIdentifier";
NSString *const WMFEditRevertedReadMoreActionIdentifier = @"WMFEditRevertedReadMoreActionIdentifier";
NSString *const WMFEditRevertedNotificationIDKey = @"WMFEditRevertedNotificationIDKey";

NSString *const WMFNotificationInfoArticleTitleKey = @"articleTitle";
NSString *const WMFNotificationInfoArticleURLStringKey = @"articleURLString";
NSString *const WMFNotificationInfoThumbnailURLStringKey = @"thumbnailURLString";
Expand Down Expand Up @@ -93,9 +89,8 @@ - (void)updateCategories {
UNNotificationAction *readNowAction = [UNNotificationAction actionWithIdentifier:WMFInTheNewsNotificationReadNowActionIdentifier title:WMFLocalizedStringWithDefaultValue(@"in-the-news-notification-read-now-action-title", nil, nil, @"Read Now", @"Title on the 'Read Now' action button") options:UNNotificationActionOptionForeground];
UNNotificationAction *shareAction = [UNNotificationAction actionWithIdentifier:WMFInTheNewsNotificationShareActionIdentifier title:WMFLocalizedStringWithDefaultValue(@"in-the-news-notification-share-action-title", nil, nil, @"Share…", @"Title on the 'Share' action button {{Identical|Share}}") options:UNNotificationActionOptionForeground];
UNNotificationAction *saveForLaterAction = [UNNotificationAction actionWithIdentifier:WMFInTheNewsNotificationSaveForLaterActionIdentifier title:WMFLocalizedStringWithDefaultValue(@"in-the-news-notification-save-for-later-action-title", nil, nil, @"Save for later", @"Title on the 'Save for later' action button") options:UNNotificationActionOptionNone];
UNNotificationAction *readMoreAction = [UNNotificationAction actionWithIdentifier:WMFEditRevertedReadMoreActionIdentifier title:WMFLocalizedStringWithDefaultValue(@"reverted-edit-notification-read-more-action-title", nil, nil, @"Read more", @"Title on the 'Read more' action button") options:UNNotificationActionOptionNone];

if (!readNowAction || !saveForLaterAction || !shareAction || !readMoreAction) {
if (!readNowAction || !saveForLaterAction || !shareAction) {
DDLogError(@"Unable to create notification categories");
return;
}
Expand All @@ -107,9 +102,7 @@ - (void)updateCategories {
return;
}

UNNotificationCategory *editRevertedCategory = [UNNotificationCategory categoryWithIdentifier:WMFEditRevertedNotificationCategoryIdentifier actions:@[readMoreAction] intentIdentifiers:@[] options:UNNotificationCategoryOptionNone];

[center setNotificationCategories:[NSSet setWithObjects:inTheNewsCategory, editRevertedCategory, nil]];
[center setNotificationCategories:[NSSet setWithObjects:inTheNewsCategory, nil]];
}

- (void)requestPermissionsWithCompletionHandler:(void (^)(BOOL, NSError *_Nullable))completionHandler {
Expand Down
2 changes: 0 additions & 2 deletions Wikipedia/Localizations/en.lproj/Localizable.strings
Expand Up @@ -734,8 +734,6 @@
"replace-textfield-accessibility" = "Replace";
"replace-textfield-placeholder" = "Replace with…";
"reverted-edit-back-to-article-button-title" = "Back to article";
"reverted-edit-notification-body" = "The edit you made of the article $1 was reverted by $2";
"reverted-edit-notification-read-more-action-title" = "Read more";
"reverted-edit-possible-reasons" = "- Your contribution didn’t follow one of the guidelines. $1 \n\n - Your contribution looked like an experiment or vandalism";
"reverted-edit-possible-reasons-subtitle" = "We know that you tried your best, but one of the reviewers had a concern.\n\nPossible reasons your edit was reverted include:";
"reverted-edit-thanks-for-editing-title" = "Thanks for editing Wikipedia!";
Expand Down
2 changes: 0 additions & 2 deletions Wikipedia/Localizations/qqq.lproj/Localizable.strings
Expand Up @@ -734,8 +734,6 @@
"replace-textfield-accessibility" = "Accessibility label for the replace text field.";
"replace-textfield-placeholder" = "Placeholder text seen in replace textfield before textfield is focused.";
"reverted-edit-back-to-article-button-title" = "Title for button that allows the user to go back to the article they edited";
"reverted-edit-notification-body" = "Title for notification telling user that the edit they made was reverted. $1 is replaced with the title of the affected article, $2 is replaced with the username of the person who reverted the edit.";
"reverted-edit-notification-read-more-action-title" = "Title on the 'Read more' action button";
"reverted-edit-possible-reasons" = "List of possible reasons describing why an edit might have been reverted. $1 is replaced with a link to view editing guidelines";
"reverted-edit-possible-reasons-subtitle" = "Subtitle leading to an explanation why an edit was reverted.";
"reverted-edit-thanks-for-editing-title" = "Title thanking the user for contributing to Wikipedia";
Expand Down
Binary file modified Wikipedia/iOS Native Localizations/ar.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/as.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/bs.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/ca.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/ckb.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/cs.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/da.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/de.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/en.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/eo.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/es.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/fa.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/fi.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/fr.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/ga.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/gl.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/he.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/hu.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/id.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/is.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/it.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/ja.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/jv.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/ko.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/lb.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/lv.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/mk.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/ms.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/my.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/nb.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/ne.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/nl.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/pl.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/pt-br.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/pt.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/ro.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/ru.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/sd.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/sk.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/sr-EC.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/sr-el.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/sv.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/te.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/th.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/tr.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/uk.lproj/Localizable.strings
Binary file not shown.
Binary file modified Wikipedia/iOS Native Localizations/vi.lproj/Localizable.strings
Binary file not shown.
Binary file not shown.
Binary file not shown.