Skip to content

Commit

Permalink
Merge pull request #18772 from wordpress-mobile/feature/18761-reminde…
Browse files Browse the repository at this point in the history
…r-schedule-coordinator

Blogging Prompts: Add wrapper component to interface between schedulers
  • Loading branch information
dvdchr authored May 30, 2022
2 parents 2ca241d + bf4c0c8 commit d5cb45a
Show file tree
Hide file tree
Showing 3 changed files with 466 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import UserNotifications

/// Bridges the logic between Blogging Reminders and Blogging Prompts.
///
/// Users can switch between receiving Blogging Reminders or Blogging Prompts based on the switch toggle in the reminder sheet. They are both delivered
/// through local notifications, but the mechanism between the two is differentiated due to technical limitations. Blogging Prompts requires the content
/// of each notification to be different, and this is not possible if we want to use a repeating `UNCalendarNotificationTrigger`.
///
class ReminderScheduleCoordinator {

// MARK: Dependencies

private let bloggingRemindersScheduler: BloggingRemindersScheduler
private let promptRemindersScheduler: PromptRemindersScheduler
private let bloggingPromptsServiceFactory: BloggingPromptsServiceFactory

// MARK: Public Methods

init(bloggingRemindersScheduler: BloggingRemindersScheduler,
promptRemindersScheduler: PromptRemindersScheduler,
bloggingPromptsServiceFactory: BloggingPromptsServiceFactory = .init()) {
self.bloggingRemindersScheduler = bloggingRemindersScheduler
self.promptRemindersScheduler = promptRemindersScheduler
self.bloggingPromptsServiceFactory = bloggingPromptsServiceFactory
}

convenience init(notificationScheduler: NotificationScheduler = UNUserNotificationCenter.current(),
pushNotificationAuthorizer: PushNotificationAuthorizer = InteractiveNotificationsManager.shared,
bloggingPromptsServiceFactory: BloggingPromptsServiceFactory = .init()) throws {

let bloggingRemindersScheduler = try BloggingRemindersScheduler(notificationCenter: notificationScheduler,
pushNotificationAuthorizer: pushNotificationAuthorizer)
let promptRemindersScheduler = PromptRemindersScheduler(bloggingPromptsServiceFactory: bloggingPromptsServiceFactory,
notificationScheduler: notificationScheduler,
pushAuthorizer: pushNotificationAuthorizer)

self.init(bloggingRemindersScheduler: bloggingRemindersScheduler,
promptRemindersScheduler: promptRemindersScheduler,
bloggingPromptsServiceFactory: bloggingPromptsServiceFactory)
}

/// Returns the user's reminder schedule for the given `blog`, based on the current reminder type.
///
/// - Parameter blog: The blog associated with the reminders.
/// - Returns: The user's preferred reminder schedule.
func schedule(for blog: Blog) -> BloggingRemindersScheduler.Schedule {
switch reminderType(for: blog) {
case .bloggingReminders:
return bloggingRemindersScheduler.schedule(for: blog)

case .bloggingPrompts:
guard let settings = promptReminderSettings(for: blog),
let reminderDays = settings.reminderDays,
!reminderDays.getActiveWeekdays().isEmpty else {
return .none
}

return .weekdays(reminderDays.getActiveWeekdays())
}
}

/// Returns the user's preferred time for the given `blog`, based on the current reminder type.
///
/// - Parameter blog: The blog associated with the reminders.
/// - Returns: The user's preferred time returned in `Date`.
func scheduledTime(for blog: Blog) -> Date {
switch reminderType(for: blog) {
case .bloggingReminders:
return bloggingRemindersScheduler.scheduledTime(for: blog)

case .bloggingPrompts:
guard let settings = promptReminderSettings(for: blog),
let dateForTime = settings.reminderTimeDate() else {
return Constants.defaultTime
}

return dateForTime
}
}

/// Schedules a reminder notification for the given `blog` based on the current reminder type.
///
/// - Note: Calling this method will trigger the push notification authorization flow.
///
/// - Parameters:
/// - schedule: The preferred notification schedule.
/// - blog: The blog that will upload the user's post.
/// - time: The user's preferred time to be notified.
/// - completion: Closure called after the process completes.
func schedule(_ schedule: BloggingRemindersScheduler.Schedule,
for blog: Blog,
time: Date? = nil,
completion: @escaping (Result<Void, Swift.Error>) -> ()) {
switch reminderType(for: blog) {
case .bloggingReminders:
bloggingRemindersScheduler.schedule(schedule, for: blog, time: time) { [weak self] result in
// always unschedule prompt reminders in case the user toggled the switch.
self?.promptRemindersScheduler.unschedule(for: blog)
completion(result)
}

case .bloggingPrompts:
promptRemindersScheduler.schedule(schedule, for: blog, time: time) { [weak self] result in
// always unschedule blogging reminders in case the user toggled the switch.
self?.bloggingRemindersScheduler.unschedule(for: blog)
completion(result)
}
}
}

/// Unschedules all future reminders from the given `blog`.
/// This applies to both Blogging Reminders and Blogging Prompts.
///
/// - Parameter blog: The blog associated with the reminders.
func unschedule(for blog: Blog) {
bloggingRemindersScheduler.unschedule(for: blog)
promptRemindersScheduler.unschedule(for: blog)
}

}

// MARK: - Private Helpers

private extension ReminderScheduleCoordinator {

enum ReminderType {
case bloggingReminders
case bloggingPrompts
}

enum Constants {
static let defaultHour = 10
static let defaultMinute = 0

static var defaultTime: Date {
let calendar = Calendar.current
return calendar.date(from: DateComponents(calendar: calendar, hour: defaultHour, minute: defaultMinute)) ?? Date()
}
}

func promptReminderSettings(for blog: Blog) -> BloggingPromptSettings? {
guard let service = bloggingPromptsServiceFactory.makeService(for: blog) else {
return nil
}

return service.localSettings
}

func reminderType(for blog: Blog) -> ReminderType {
if Feature.enabled(.bloggingPrompts),
let settings = promptReminderSettings(for: blog),
settings.promptRemindersEnabled {
return .bloggingPrompts
}

return .bloggingReminders
}
}
20 changes: 19 additions & 1 deletion WordPress/WordPress.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -4626,6 +4626,7 @@
FE25C235271F23000084E1DB /* ReaderCommentsNotificationSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE25C234271F23000084E1DB /* ReaderCommentsNotificationSheetViewController.swift */; };
FE25C236271F23000084E1DB /* ReaderCommentsNotificationSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE25C234271F23000084E1DB /* ReaderCommentsNotificationSheetViewController.swift */; };
FE2E3729281C839C00A1E82A /* BloggingPromptsServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE2E3728281C839C00A1E82A /* BloggingPromptsServiceTests.swift */; };
FE32E7F12844971000744D80 /* ReminderScheduleCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE32E7F02844971000744D80 /* ReminderScheduleCoordinatorTests.swift */; };
FE32EFFF275914390040BE67 /* MenuSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE32EFFE275914390040BE67 /* MenuSheetViewController.swift */; };
FE32F000275914390040BE67 /* MenuSheetViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE32EFFE275914390040BE67 /* MenuSheetViewController.swift */; };
FE32F002275F602E0040BE67 /* CommentContentRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE32F001275F602E0040BE67 /* CommentContentRenderer.swift */; };
Expand Down Expand Up @@ -4680,6 +4681,8 @@
FEDA1AD9269D475D0038EC98 /* ListTableViewCell+Comments.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDA1AD7269D475D0038EC98 /* ListTableViewCell+Comments.swift */; };
FEDDD46F26A03DE900F8942B /* ListTableViewCell+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDDD46E26A03DE900F8942B /* ListTableViewCell+Notifications.swift */; };
FEDDD47026A03DE900F8942B /* ListTableViewCell+Notifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEDDD46E26A03DE900F8942B /* ListTableViewCell+Notifications.swift */; };
FEF4DC5528439357003806BE /* ReminderScheduleCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEF4DC5428439357003806BE /* ReminderScheduleCoordinator.swift */; };
FEF4DC5628439357003806BE /* ReminderScheduleCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEF4DC5428439357003806BE /* ReminderScheduleCoordinator.swift */; };
FEFA263E26C58427009CCB7E /* ShareAppTextActivityItemSourceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FEFA263D26C58427009CCB7E /* ShareAppTextActivityItemSourceTests.swift */; };
FEFA263F26C5AE9A009CCB7E /* ShareAppContentPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE06AC8226C3BD0900B69DE4 /* ShareAppContentPresenter.swift */; };
FEFA264026C5AE9E009CCB7E /* ShareAppTextActivityItemSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE06AC8426C3C2F800B69DE4 /* ShareAppTextActivityItemSource.swift */; };
Expand Down Expand Up @@ -7932,6 +7935,7 @@
FE23EB4826E7C91F005A1698 /* richCommentStyle.css */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.css; name = richCommentStyle.css; path = Resources/HTML/richCommentStyle.css; sourceTree = "<group>"; };
FE25C234271F23000084E1DB /* ReaderCommentsNotificationSheetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReaderCommentsNotificationSheetViewController.swift; sourceTree = "<group>"; };
FE2E3728281C839C00A1E82A /* BloggingPromptsServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BloggingPromptsServiceTests.swift; sourceTree = "<group>"; };
FE32E7F02844971000744D80 /* ReminderScheduleCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReminderScheduleCoordinatorTests.swift; sourceTree = "<group>"; };
FE32EFFE275914390040BE67 /* MenuSheetViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuSheetViewController.swift; sourceTree = "<group>"; };
FE32F001275F602E0040BE67 /* CommentContentRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentContentRenderer.swift; sourceTree = "<group>"; };
FE32F005275F62620040BE67 /* WebCommentContentRenderer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebCommentContentRenderer.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -7964,6 +7968,7 @@
FECA44312836647100D01F15 /* PromptRemindersSchedulerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PromptRemindersSchedulerTests.swift; sourceTree = "<group>"; };
FEDA1AD7269D475D0038EC98 /* ListTableViewCell+Comments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ListTableViewCell+Comments.swift"; sourceTree = "<group>"; };
FEDDD46E26A03DE900F8942B /* ListTableViewCell+Notifications.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ListTableViewCell+Notifications.swift"; sourceTree = "<group>"; };
FEF4DC5428439357003806BE /* ReminderScheduleCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReminderScheduleCoordinator.swift; sourceTree = "<group>"; };
FEF93A01F06919BDB9486A8E /* Pods-WordPressHomeWidgetToday.release-alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressHomeWidgetToday.release-alpha.xcconfig"; path = "../Pods/Target Support Files/Pods-WordPressHomeWidgetToday/Pods-WordPressHomeWidgetToday.release-alpha.xcconfig"; sourceTree = "<group>"; };
FEFA263D26C58427009CCB7E /* ShareAppTextActivityItemSourceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareAppTextActivityItemSourceTests.swift; sourceTree = "<group>"; };
FEFC0F872730510F001F7F1D /* WordPress 136.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "WordPress 136.xcdatamodel"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -11470,6 +11475,7 @@
children = (
DC06DFF727BD52A100969974 /* BackgroundTasks */,
F15D1FB8265C40EA00854EE5 /* Blogging Reminders */,
FE32E7EF284496F500744D80 /* Blogging Prompts */,
174C116D2624601000346EC6 /* Deep Linking */,
F93735F422D53C1800A3C312 /* Logging */,
93B853211B44165B0064FE72 /* Analytics */,
Expand Down Expand Up @@ -14899,7 +14905,6 @@
children = (
F15D1FB9265C41A900854EE5 /* BloggingRemindersStoreTests.swift */,
F151EC822665271200AEA89E /* BloggingRemindersSchedulerTests.swift */,
FECA44312836647100D01F15 /* PromptRemindersSchedulerTests.swift */,
);
name = "Blogging Reminders";
sourceTree = "<group>";
Expand Down Expand Up @@ -15431,6 +15436,15 @@
path = Prompts;
sourceTree = "<group>";
};
FE32E7EF284496F500744D80 /* Blogging Prompts */ = {
isa = PBXGroup;
children = (
FECA44312836647100D01F15 /* PromptRemindersSchedulerTests.swift */,
FE32E7F02844971000744D80 /* ReminderScheduleCoordinatorTests.swift */,
);
name = "Blogging Prompts";
sourceTree = "<group>";
};
FE32F004275F60360040BE67 /* ContentRenderer */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -15481,6 +15495,7 @@
isa = PBXGroup;
children = (
FECA442E28350B7800D01F15 /* PromptRemindersScheduler.swift */,
FEF4DC5428439357003806BE /* ReminderScheduleCoordinator.swift */,
);
path = "Blogging Prompts";
sourceTree = "<group>";
Expand Down Expand Up @@ -19336,6 +19351,7 @@
E66969E01B9E648100EC9C00 /* ReaderTopicToReaderDefaultTopic37to38.swift in Sources */,
F1DB8D2B2288C24500906E2F /* UploadsManager.swift in Sources */,
9856A3E4261FD27A008D6354 /* UserProfileSectionHeader.swift in Sources */,
FEF4DC5528439357003806BE /* ReminderScheduleCoordinator.swift in Sources */,
82A062DE2017BCBA0084CE7C /* ActivityListSectionHeaderView.swift in Sources */,
73FEC871220B358500CEF791 /* WPAccount+RestApi.swift in Sources */,
93CDC72126CD342900C8A3A8 /* DestructiveAlertHelper.swift in Sources */,
Expand Down Expand Up @@ -20071,6 +20087,7 @@
E1C545801C6C79BB001CEB0E /* MediaSettingsTests.swift in Sources */,
C3439B5F27FE3A3C0058DA55 /* SiteCreationWizardLauncherTests.swift in Sources */,
7E987F5A2108122A00CAFB88 /* NotificationUtility.swift in Sources */,
FE32E7F12844971000744D80 /* ReminderScheduleCoordinatorTests.swift in Sources */,
4688E6CC26AB571D00A5D894 /* RequestAuthenticatorTests.swift in Sources */,
7E442FC720F677CB00DEACA5 /* ActivityLogRangesTest.swift in Sources */,
938466B92683CA0E00A538DC /* ReferrerDetailsViewModelTests.swift in Sources */,
Expand Down Expand Up @@ -20674,6 +20691,7 @@
FABB225C2602FC2C00C8785C /* MenuHeaderViewController.m in Sources */,
FABB225D2602FC2C00C8785C /* NotificationDetailsViewController.swift in Sources */,
C3234F5527EBBACA004ADB29 /* SiteIntentVertical.swift in Sources */,
FEF4DC5628439357003806BE /* ReminderScheduleCoordinator.swift in Sources */,
FABB225F2602FC2C00C8785C /* MediaURLExporter.swift in Sources */,
FABB22602602FC2C00C8785C /* WordPress.xcdatamodeld in Sources */,
93CDC72226CD342900C8A3A8 /* DestructiveAlertHelper.swift in Sources */,
Expand Down
Loading

0 comments on commit d5cb45a

Please sign in to comment.