Skip to content

Commit

Permalink
chore: notifications preferences screen implementation with edge cases (
Browse files Browse the repository at this point in the history
#111)

* chore: notifications preferences screen implementation with edge cases

* refactor: address review feedback

* refactor: address review feedback

* chore: implement seen and read APIs

* refactor: address review feedback

* refactor: address review feedback

* fix: address review feedback

* chore: fix spelling mistakes

* chore: fix indentation

* chore: present system alert for push notifications

* chore: set optional handler parameter to nil

* chore: fix ui issues on ipad

* chore: minor refactoring

* chore: make ui consistent with other settings screens

---------

Co-authored-by: Tayyab Akram <[email protected]>
  • Loading branch information
saeedbashir and mta452 authored Jan 14, 2025
1 parent cb01469 commit 57bf369
Show file tree
Hide file tree
Showing 28 changed files with 1,088 additions and 136 deletions.
36 changes: 36 additions & 0 deletions Authorization/AuthorizationTests/AuthorizationMock.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -999,6 +999,13 @@ open class AuthorizationRouterMock: AuthorizationRouter, Mock {
perform?()
}

@MainActor
open func performNotificationRegistration() {
addInvocation(.m_performNotificationRegistration)
let perform = methodPerformValue(.m_performNotificationRegistration) as? () -> Void
perform?()
}


fileprivate enum MethodType {
case m_showUpdateRequiredView__showAccountLink_showAccountLink(Parameter<Bool>)
Expand All @@ -1025,6 +1032,7 @@ open class AuthorizationRouterMock: AuthorizationRouter, Mock {
case m_hideUpgradeLoaderView__animated_animated(Parameter<Bool>)
case m_showRestoreProgressView
case m_hideRestoreProgressView
case m_performNotificationRegistration

static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult {
switch (lhs, rhs) {
Expand Down Expand Up @@ -1159,6 +1167,8 @@ open class AuthorizationRouterMock: AuthorizationRouter, Mock {
case (.m_showRestoreProgressView, .m_showRestoreProgressView): return .match

case (.m_hideRestoreProgressView, .m_hideRestoreProgressView): return .match

case (.m_performNotificationRegistration, .m_performNotificationRegistration): return .match
default: return .none
}
}
Expand Down Expand Up @@ -1189,6 +1199,7 @@ open class AuthorizationRouterMock: AuthorizationRouter, Mock {
case let .m_hideUpgradeLoaderView__animated_animated(p0): return p0.intValue
case .m_showRestoreProgressView: return 0
case .m_hideRestoreProgressView: return 0
case .m_performNotificationRegistration: return 0
}
}
func assertionName() -> String {
Expand Down Expand Up @@ -1217,6 +1228,7 @@ open class AuthorizationRouterMock: AuthorizationRouter, Mock {
case .m_hideUpgradeLoaderView__animated_animated: return ".hideUpgradeLoaderView(animated:)"
case .m_showRestoreProgressView: return ".showRestoreProgressView()"
case .m_hideRestoreProgressView: return ".hideRestoreProgressView()"
case .m_performNotificationRegistration: return ".performNotificationRegistration()"
}
}
}
Expand Down Expand Up @@ -1265,6 +1277,8 @@ open class AuthorizationRouterMock: AuthorizationRouter, Mock {
public static func showRestoreProgressView() -> Verify { return Verify(method: .m_showRestoreProgressView)}
@MainActor
public static func hideRestoreProgressView() -> Verify { return Verify(method: .m_hideRestoreProgressView)}
@MainActor
public static func performNotificationRegistration() -> Verify { return Verify(method: .m_performNotificationRegistration)}
}

public struct Perform {
Expand Down Expand Up @@ -1349,6 +1363,10 @@ open class AuthorizationRouterMock: AuthorizationRouter, Mock {
public static func hideRestoreProgressView(perform: @escaping () -> Void) -> Perform {
return Perform(method: .m_hideRestoreProgressView, performs: perform)
}
@MainActor
public static func performNotificationRegistration(perform: @escaping () -> Void) -> Perform {
return Perform(method: .m_performNotificationRegistration, performs: perform)
}
}

public func given(_ method: Given) {
Expand Down Expand Up @@ -1612,6 +1630,13 @@ open class BaseRouterMock: BaseRouter, Mock {
perform?()
}

@MainActor
open func performNotificationRegistration() {
addInvocation(.m_performNotificationRegistration)
let perform = methodPerformValue(.m_performNotificationRegistration) as? () -> Void
perform?()
}


fileprivate enum MethodType {
case m_backToRoot__animated_animated(Parameter<Bool>)
Expand All @@ -1637,6 +1662,7 @@ open class BaseRouterMock: BaseRouter, Mock {
case m_hideUpgradeLoaderView__animated_animated(Parameter<Bool>)
case m_showRestoreProgressView
case m_hideRestoreProgressView
case m_performNotificationRegistration

static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult {
switch (lhs, rhs) {
Expand Down Expand Up @@ -1766,6 +1792,8 @@ open class BaseRouterMock: BaseRouter, Mock {
case (.m_showRestoreProgressView, .m_showRestoreProgressView): return .match

case (.m_hideRestoreProgressView, .m_hideRestoreProgressView): return .match

case (.m_performNotificationRegistration, .m_performNotificationRegistration): return .match
default: return .none
}
}
Expand Down Expand Up @@ -1795,6 +1823,7 @@ open class BaseRouterMock: BaseRouter, Mock {
case let .m_hideUpgradeLoaderView__animated_animated(p0): return p0.intValue
case .m_showRestoreProgressView: return 0
case .m_hideRestoreProgressView: return 0
case .m_performNotificationRegistration: return 0
}
}
func assertionName() -> String {
Expand Down Expand Up @@ -1822,6 +1851,7 @@ open class BaseRouterMock: BaseRouter, Mock {
case .m_hideUpgradeLoaderView__animated_animated: return ".hideUpgradeLoaderView(animated:)"
case .m_showRestoreProgressView: return ".showRestoreProgressView()"
case .m_hideRestoreProgressView: return ".hideRestoreProgressView()"
case .m_performNotificationRegistration: return ".performNotificationRegistration()"
}
}
}
Expand Down Expand Up @@ -1869,6 +1899,8 @@ open class BaseRouterMock: BaseRouter, Mock {
public static func showRestoreProgressView() -> Verify { return Verify(method: .m_showRestoreProgressView)}
@MainActor
public static func hideRestoreProgressView() -> Verify { return Verify(method: .m_hideRestoreProgressView)}
@MainActor
public static func performNotificationRegistration() -> Verify { return Verify(method: .m_performNotificationRegistration)}
}

public struct Perform {
Expand Down Expand Up @@ -1950,6 +1982,10 @@ open class BaseRouterMock: BaseRouter, Mock {
public static func hideRestoreProgressView(perform: @escaping () -> Void) -> Perform {
return Perform(method: .m_hideRestoreProgressView, performs: perform)
}
@MainActor
public static func performNotificationRegistration(perform: @escaping () -> Void) -> Perform {
return Perform(method: .m_performNotificationRegistration, performs: perform)
}
}

public func given(_ method: Given) {
Expand Down
6 changes: 6 additions & 0 deletions Core/Core/Configuration/BaseRouter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ public protocol BaseRouter {

@MainActor
func hideRestoreProgressView()

@MainActor
func performNotificationRegistration()
}

extension BaseRouter {
Expand Down Expand Up @@ -179,5 +182,8 @@ open class BaseRouterMock: BaseRouter {

@MainActor
public func hideRestoreProgressView() {}

@MainActor
public func performNotificationRegistration() {}
}
#endif
2 changes: 2 additions & 0 deletions Core/Core/Data/CoreStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public protocol CoreStorage {
var userSettings: UserSettings? {get set}
var resetAppSupportDirectoryUserData: Bool? {get set}
var lastUsedSocialAuth: String? {get set}
var discussionNotificationsSettingStatus: Bool? {get set}
func clear()
}

Expand All @@ -37,6 +38,7 @@ public class CoreStorageMock: CoreStorage {
public var userSettings: UserSettings?
public var resetAppSupportDirectoryUserData: Bool?
public var lastUsedSocialAuth: String?
public var discussionNotificationsSettingStatus: Bool?
public func clear() {}

public init() {}
Expand Down
2 changes: 2 additions & 0 deletions Core/Core/Extensions/Notification.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ public extension Notification.Name {
static let courseUpgradeCompletionNotification = Notification.Name("CourseUpgradeCompletionNotification")
static let getCourseDates = Notification.Name("getCourseDates")
static let refreshEnrollments = Notification.Name("refreshEnrollments")
static let notificationRegistration = Notification.Name("notificationRegistration")
}

public extension Notification {
enum UserInfoKey: String {
case isForced
case status
}
}
18 changes: 18 additions & 0 deletions Course/CourseTests/CourseMock.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,13 @@ open class BaseRouterMock: BaseRouter, Mock {
perform?()
}

@MainActor
open func performNotificationRegistration() {
addInvocation(.m_performNotificationRegistration)
let perform = methodPerformValue(.m_performNotificationRegistration) as? () -> Void
perform?()
}


fileprivate enum MethodType {
case m_backToRoot__animated_animated(Parameter<Bool>)
Expand All @@ -678,6 +685,7 @@ open class BaseRouterMock: BaseRouter, Mock {
case m_hideUpgradeLoaderView__animated_animated(Parameter<Bool>)
case m_showRestoreProgressView
case m_hideRestoreProgressView
case m_performNotificationRegistration

static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult {
switch (lhs, rhs) {
Expand Down Expand Up @@ -807,6 +815,8 @@ open class BaseRouterMock: BaseRouter, Mock {
case (.m_showRestoreProgressView, .m_showRestoreProgressView): return .match

case (.m_hideRestoreProgressView, .m_hideRestoreProgressView): return .match

case (.m_performNotificationRegistration, .m_performNotificationRegistration): return .match
default: return .none
}
}
Expand Down Expand Up @@ -836,6 +846,7 @@ open class BaseRouterMock: BaseRouter, Mock {
case let .m_hideUpgradeLoaderView__animated_animated(p0): return p0.intValue
case .m_showRestoreProgressView: return 0
case .m_hideRestoreProgressView: return 0
case .m_performNotificationRegistration: return 0
}
}
func assertionName() -> String {
Expand Down Expand Up @@ -863,6 +874,7 @@ open class BaseRouterMock: BaseRouter, Mock {
case .m_hideUpgradeLoaderView__animated_animated: return ".hideUpgradeLoaderView(animated:)"
case .m_showRestoreProgressView: return ".showRestoreProgressView()"
case .m_hideRestoreProgressView: return ".hideRestoreProgressView()"
case .m_performNotificationRegistration: return ".performNotificationRegistration()"
}
}
}
Expand Down Expand Up @@ -910,6 +922,8 @@ open class BaseRouterMock: BaseRouter, Mock {
public static func showRestoreProgressView() -> Verify { return Verify(method: .m_showRestoreProgressView)}
@MainActor
public static func hideRestoreProgressView() -> Verify { return Verify(method: .m_hideRestoreProgressView)}
@MainActor
public static func performNotificationRegistration() -> Verify { return Verify(method: .m_performNotificationRegistration)}
}

public struct Perform {
Expand Down Expand Up @@ -991,6 +1005,10 @@ open class BaseRouterMock: BaseRouter, Mock {
public static func hideRestoreProgressView(perform: @escaping () -> Void) -> Perform {
return Perform(method: .m_hideRestoreProgressView, performs: perform)
}
@MainActor
public static func performNotificationRegistration(perform: @escaping () -> Void) -> Perform {
return Perform(method: .m_performNotificationRegistration, performs: perform)
}
}

public func given(_ method: Given) {
Expand Down
50 changes: 26 additions & 24 deletions Dashboard/Dashboard/Presentation/PrimaryCourseDashboardView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -331,31 +331,33 @@ public struct PrimaryCourseDashboardView<ProgramView: View>: View {
.foregroundColor(Theme.Colors.textPrimary)
.accessibilityIdentifier("courses_header_text")
Spacer()
Button(action: {
router.showNotificationsScreen()
}, label: {
CoreAssets.notificationsIcon.swiftUIImage
.renderingMode(.template)
.foregroundColor(Theme.Colors.accentColor)
})
.frame(width: 24, height: 24)
.overlay {
if viewModel.hasUnreadNotifications {
if #available(iOS 17.0, *) {
Circle()
.stroke(Theme.Colors.background, lineWidth: 5)
.fill(Theme.Colors.accentButtonColor)
.frame(width: 8, height: 8)
.offset(x: 7, y: -6)
} else {
Circle()
.strokeBorder(Theme.Colors.background, lineWidth: 2)
.frame(width: 10, height: 10)
.background(
if viewModel.config.pushNotificationsEnabled {
Button(action: {
router.showNotificationsScreen()
}, label: {
CoreAssets.notificationsIcon.swiftUIImage
.renderingMode(.template)
.foregroundColor(Theme.Colors.accentColor)
})
.frame(width: 24, height: 24)
.overlay {
if viewModel.hasUnreadNotifications {
if #available(iOS 17.0, *) {
Circle()
.foregroundColor(Theme.Colors.accentButtonColor)
)
.offset(x: 5, y: -6)
.stroke(Theme.Colors.background, lineWidth: 5)
.fill(Theme.Colors.accentButtonColor)
.frame(width: 8, height: 8)
.offset(x: 7, y: -6)
} else {
Circle()
.strokeBorder(Theme.Colors.background, lineWidth: 2)
.frame(width: 10, height: 10)
.background(
Circle()
.foregroundColor(Theme.Colors.accentButtonColor)
)
.offset(x: 5, y: -6)
}
}
}
}
Expand Down
18 changes: 18 additions & 0 deletions Dashboard/DashboardTests/DashboardMock.generated.swift
Original file line number Diff line number Diff line change
Expand Up @@ -653,6 +653,13 @@ open class BaseRouterMock: BaseRouter, Mock {
perform?()
}

@MainActor
open func performNotificationRegistration() {
addInvocation(.m_performNotificationRegistration)
let perform = methodPerformValue(.m_performNotificationRegistration) as? () -> Void
perform?()
}


fileprivate enum MethodType {
case m_backToRoot__animated_animated(Parameter<Bool>)
Expand All @@ -678,6 +685,7 @@ open class BaseRouterMock: BaseRouter, Mock {
case m_hideUpgradeLoaderView__animated_animated(Parameter<Bool>)
case m_showRestoreProgressView
case m_hideRestoreProgressView
case m_performNotificationRegistration

static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult {
switch (lhs, rhs) {
Expand Down Expand Up @@ -807,6 +815,8 @@ open class BaseRouterMock: BaseRouter, Mock {
case (.m_showRestoreProgressView, .m_showRestoreProgressView): return .match

case (.m_hideRestoreProgressView, .m_hideRestoreProgressView): return .match

case (.m_performNotificationRegistration, .m_performNotificationRegistration): return .match
default: return .none
}
}
Expand Down Expand Up @@ -836,6 +846,7 @@ open class BaseRouterMock: BaseRouter, Mock {
case let .m_hideUpgradeLoaderView__animated_animated(p0): return p0.intValue
case .m_showRestoreProgressView: return 0
case .m_hideRestoreProgressView: return 0
case .m_performNotificationRegistration: return 0
}
}
func assertionName() -> String {
Expand Down Expand Up @@ -863,6 +874,7 @@ open class BaseRouterMock: BaseRouter, Mock {
case .m_hideUpgradeLoaderView__animated_animated: return ".hideUpgradeLoaderView(animated:)"
case .m_showRestoreProgressView: return ".showRestoreProgressView()"
case .m_hideRestoreProgressView: return ".hideRestoreProgressView()"
case .m_performNotificationRegistration: return ".performNotificationRegistration()"
}
}
}
Expand Down Expand Up @@ -910,6 +922,8 @@ open class BaseRouterMock: BaseRouter, Mock {
public static func showRestoreProgressView() -> Verify { return Verify(method: .m_showRestoreProgressView)}
@MainActor
public static func hideRestoreProgressView() -> Verify { return Verify(method: .m_hideRestoreProgressView)}
@MainActor
public static func performNotificationRegistration() -> Verify { return Verify(method: .m_performNotificationRegistration)}
}

public struct Perform {
Expand Down Expand Up @@ -991,6 +1005,10 @@ open class BaseRouterMock: BaseRouter, Mock {
public static func hideRestoreProgressView(perform: @escaping () -> Void) -> Perform {
return Perform(method: .m_hideRestoreProgressView, performs: perform)
}
@MainActor
public static func performNotificationRegistration(perform: @escaping () -> Void) -> Perform {
return Perform(method: .m_performNotificationRegistration, performs: perform)
}
}

public func given(_ method: Given) {
Expand Down
Loading

0 comments on commit 57bf369

Please sign in to comment.