Skip to content

Commit

Permalink
chore: Notification preference settings and relevant code (#104)
Browse files Browse the repository at this point in the history
  • Loading branch information
saeedbashir authored Dec 20, 2024
1 parent 66593fa commit 1cec44b
Show file tree
Hide file tree
Showing 28 changed files with 344 additions and 36 deletions.
2 changes: 1 addition & 1 deletion Core/Core.xcodeproj/xcshareddata/xcschemes/Core.xcscheme
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1610"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
5 changes: 5 additions & 0 deletions Core/Core/Analytics/CoreAnalytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ public enum AnalyticsEvent: String {
case videoStreamQualityChanged = "Video:Streaming Quality Changed"
case videoDownloadQualityChanged = "Video:Download Quality Changed"
case profileVideoSettingsClicked = "Profile:Video Setting Clicked"
case profilePushSettingsClicked = "Profile:Push Notifications Setting Clicked"
case privacyPolicyClicked = "Profile:Privacy Policy Clicked"
case cookiePolicyClicked = "Profile:Cookie Policy Clicked"
case emailSupportClicked = "Profile:Contact Support Clicked"
Expand Down Expand Up @@ -332,6 +333,7 @@ public enum AnalyticsEvent: String {
case discussionLikeToggle = "Discussion:Like Toggle"
case discussionReportToggle = "Discussion:Report Toggle"
case notificationSettingPermissionStatus = "Notification:Setting Permission Status"
case notificationDiscussionPermissionToggle = "Notification:Discussion Permission Toggle"

}

Expand Down Expand Up @@ -363,6 +365,7 @@ public enum EventBIValue: String {
case profileEditClicked = "edx.bi.app.profile.edit.clicked"
case profileEditDoneClicked = "edx.bi.app.profile.edit_done.clicked"
case profileVideoSettingsClicked = "edx.bi.app.profile.video_setting.clicked"
case profilePushSettingsClicked = "edx.bi.app.profile.push_notifications_setting.clicked"
case emailSupportClicked = "edx.bi.app.profile.email_support.clicked"
case faqClicked = "edx.bi.app.profile.faq.clicked"
case tosClicked = "edx.bi.app.profile.terms_of_use.clicked"
Expand Down Expand Up @@ -445,6 +448,7 @@ public enum EventBIValue: String {
case discussionLikeToggle = "edx.bi.app.discussion.like_toggle"
case discussionReportToggle = "edx.bi.app.discussion.report_toggle"
case notificationSettingPermissionStatus = "edx.bi.app.notification.setting_permission.status"
case notificationDiscussionPermissionToggle = "edx.bi.app.notification.discussion.permission.toggle"
}

public struct EventParamKey {
Expand Down Expand Up @@ -515,4 +519,5 @@ public struct EventCategory {
public static let video = "video"
public static let course = "course"
public static let inAppPurchases = "in_app_purchases"
public static let notifications = "notifications"
}
6 changes: 6 additions & 0 deletions Core/Core/Configuration/Config/Config.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public protocol ConfigProtocol {
var URIScheme: String { get }
var ecommerceURL: String? { get }
var fullStory: FullStoryConfig { get }
var pushNotificationsEnabled: Bool { get }
}

public enum TokenType: String {
Expand All @@ -52,6 +53,7 @@ private enum ConfigKeys: String {
case faq = "FAQ_URL"
case URIScheme = "URI_SCHEME"
case ecommerceURL = "ECOMMERCE_URL"
case pushNotificationsEnabled = "PUSH_NOTIFICATIONS_ENABLED"
}

public class Config {
Expand Down Expand Up @@ -167,6 +169,10 @@ extension Config: ConfigProtocol {
public var ecommerceURL: String? {
string(for: ConfigKeys.ecommerceURL.rawValue)
}

public var pushNotificationsEnabled: Bool {
bool(for: ConfigKeys.pushNotificationsEnabled.rawValue)
}
}

// Mark - For testing and SwiftUI preview
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1610"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1430"
LastUpgradeVersion = "1610"
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
4 changes: 4 additions & 0 deletions Notifications/Notifications.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
02F1752F2A4DA3B60019CD70 /* NotificationsAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02F1752E2A4DA3B60019CD70 /* NotificationsAnalytics.swift */; };
02F3BFDF29252F2F0051930C /* NotificationsRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 02F3BFDE29252F2F0051930C /* NotificationsRouter.swift */; };
072787AD28D34D15002E9142 /* Core.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 072787AC28D34D15002E9142 /* Core.framework */; };
149D4A9A2D0BEF7E003A2E1D /* NotificationsSettingsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 149D4A992D0BEF7B003A2E1D /* NotificationsSettingsViewModel.swift */; };
14BAE6912D0978C300EAF0E4 /* NotificationsSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 14BAE6902D0978BC00EAF0E4 /* NotificationsSettingsView.swift */; };
87E3F4AB2D8BE4FD628C91C4 /* Pods_App_Notifications.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CAFCD31A42C7E150939829CE /* Pods_App_Notifications.framework */; };
FB43867C6484A90253882035 /* Pods_App_Notifications_NotificationsTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E7FEAD4AF36405458BBBE5AB /* Pods_App_Notifications_NotificationsTests.framework */; };
Expand Down Expand Up @@ -49,6 +50,7 @@
02F3BFDE29252F2F0051930C /* NotificationsRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsRouter.swift; sourceTree = "<group>"; };
0727879928D34C03002E9142 /* Notifications.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Notifications.framework; sourceTree = BUILT_PRODUCTS_DIR; };
072787AC28D34D15002E9142 /* Core.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; path = Core.framework; sourceTree = BUILT_PRODUCTS_DIR; };
149D4A992D0BEF7B003A2E1D /* NotificationsSettingsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsSettingsViewModel.swift; sourceTree = "<group>"; };
14BAE6902D0978BC00EAF0E4 /* NotificationsSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsSettingsView.swift; sourceTree = "<group>"; };
14BAE6A82D09D01400EAF0E4 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
3CD79FA3FF160D72A5BE303D /* Pods-App-Notifications-NotificationsTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App-Notifications-NotificationsTests.release.xcconfig"; path = "Target Support Files/Pods-App-Notifications-NotificationsTests/Pods-App-Notifications-NotificationsTests.release.xcconfig"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -203,6 +205,7 @@
1402A0C62B61011D00A0A00B /* Settings */ = {
isa = PBXGroup;
children = (
149D4A992D0BEF7B003A2E1D /* NotificationsSettingsViewModel.swift */,
14BAE6902D0978BC00EAF0E4 /* NotificationsSettingsView.swift */,
);
path = Settings;
Expand Down Expand Up @@ -453,6 +456,7 @@
0284DC0328D4922900830893 /* NotificationsRepository.swift in Sources */,
02EF39DC28D86BEF0058F6BD /* Strings.swift in Sources */,
02F1752F2A4DA3B60019CD70 /* NotificationsAnalytics.swift in Sources */,
149D4A9A2D0BEF7E003A2E1D /* NotificationsSettingsViewModel.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
10 changes: 5 additions & 5 deletions Notifications/Notifications/Domain/NotificationsInteractor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ public class NotificationsInteractor: NotificationsInteractorProtocol {
}

// Mark - For testing and SwiftUI preview
//#if DEBUG
//public extension NotificationsInteractor {
// static let mock = NotificationsInteractor(repository: NotificationsRepositoryMock())
//}
//#endif
#if DEBUG
public extension NotificationsInteractor {
static let mock = NotificationsInteractor(repository: NotificationsRepositoryMock())
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import Core

//sourcery: AutoMockable
public protocol NotificationsAnalytics {
func NotificationsScreenEvent(event: AnalyticsEvent, biValue: EventBIValue)
func notificationsScreenEvent(event: AnalyticsEvent, biValue: EventBIValue)
func notificationsDiscussionPermissionToggleEvent(action: Bool)
}

#if DEBUG
class NotificationsAnalyticsMock: NotificationsAnalytics {
public func NotificationsScreenEvent(event: AnalyticsEvent, biValue: EventBIValue) {}
public func notificationsScreenEvent(event: AnalyticsEvent, biValue: EventBIValue) {}
public func notificationsDiscussionPermissionToggleEvent(action: Bool) {}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ public protocol NotificationsRouter: BaseRouter {
// Mark - For testing and SwiftUI preview
#if DEBUG
public class NotificationsRouterMock: BaseRouterMock, NotificationsRouter {

public override init() {}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,122 @@

import SwiftUI
import Theme
import Core

public struct NotificationsSettingsView: View {
@ObservedObject
private var viewModel: NotificationsSettingsViewModel
@Environment(\.isHorizontal) private var isHorizontal

public init(viewModel: NotificationsSettingsViewModel) {
self.viewModel = viewModel
}

public var body: some View {
GeometryReader { proxy in
ZStack(alignment: .top) {
VStack {
ThemeAssets.headerBackground.swiftUIImage
.resizable()
.edgesIgnoringSafeArea(.top)
}
.frame(maxWidth: .infinity, maxHeight: 200)
.accessibilityIdentifier("header_bg_image")

VStack(alignment: .center) {
Text("Notificaion settings view")
ZStack {
HStack {
Text(NotificationsLocalization.Settings.title)
.titleSettings(color: Theme.Colors.loginNavigationText)
.accessibilityIdentifier("manage_account_text")
}
VStack {
BackNavigationButton(
color: Theme.Colors.loginNavigationText,
action: {
viewModel.router.back()
}
)
.backViewStyle()
.padding(.leading, isHorizontal ? 48 : 0)
.accessibilityIdentifier("back_button")

}.frame(minWidth: 0,
maxWidth: .infinity,
alignment: .topLeading)
}

VStack(alignment: .leading) {
HStack {
Text(NotificationsLocalization.Settings.preferenceTitle)
.font(Theme.Fonts.titleMedium)
.foregroundStyle(Theme.Colors.textPrimary)
.accessibilityIdentifier("preference_title_text")

Spacer()
Toggle(isOn: $viewModel.hasPermission, label: {})
.toggleStyle(SwitchToggleStyle(tint: Theme.Colors.toggleSwitchColor))
.frame(width: 50)
.accessibilityIdentifier("discussion_switch")
.onTapGesture {
viewModel.toggleNotificationsPermissionAction()
}

}

Text(NotificationsLocalization.Settings.preferenceDescription)
.font(Theme.Fonts.bodyMedium)
.foregroundStyle(Theme.Colors.textSecondary)
.accessibilityIdentifier("preference_description_text")

Divider()
.padding(20)
.accessibilityIdentifier("preference_divider")

Spacer()
}
.padding(20)
.roundedBackground(Theme.Colors.background)
}
.frameLimit(width: proxy.size.width)

if viewModel.showError {
VStack {
Spacer()
SnackBarView(message: viewModel.errorMessage)
.accessibilityIdentifier("preference_snack_bar")
}
.transition(.move(edge: .bottom))
.onAppear {
doAfter(Theme.Timeout.snackbarMessageLongTimeout) {
viewModel.errorMessage = nil
}
}
}
.hideNavigationBar(true)
.navigationBarBackButtonHidden(true)
.navigationTitle(NotificationsLocalization.Notifications.Settings.title)
}

.hideNavigationBar(true)
.navigationBarBackButtonHidden(true)
.navigationTitle(NotificationsLocalization.Settings.title)
}
.background(
Theme.Colors.background
.ignoresSafeArea()
)
.ignoresSafeArea(.all, edges: .horizontal)
.animation(.default, value: viewModel.showError)
}
}

#if DEBUG
struct NotificationsSettingsView_Previews: PreviewProvider {
static var previews: some View {
NotificationsSettingsView(
viewModel: NotificationsSettingsViewModel(
interactor: NotificationsInteractor.mock,
analytics: NotificationsAnalyticsMock(),
router: NotificationsRouterMock()
)
)
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
//
// NotificationsSettingsViewModel.swift
// Notifications
//
// Created by Saeed Bashir on 12/13/24.
//

import Foundation
import Core
import SwiftUI

public class NotificationsSettingsViewModel: ObservableObject {
@Published var showError: Bool = false
public var hasPermission: Bool = false
private var interactor: NotificationsInteractorProtocol
private var analytics: NotificationsAnalytics
var router: NotificationsRouter

var errorMessage: String? {
didSet {
showError = errorMessage != nil
}
}

public init(
interactor: NotificationsInteractorProtocol,
analytics: NotificationsAnalytics,
router: NotificationsRouter
) {
self.interactor = interactor
self.analytics = analytics
self.router = router
}

public func toggleNotificationsPermissionAction() {
hasPermission.toggle()
analytics.notificationsDiscussionPermissionToggleEvent(action: hasPermission)
}
}
12 changes: 7 additions & 5 deletions Notifications/Notifications/SwiftGen/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@ import Foundation
// swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
public enum NotificationsLocalization {
public enum Notifications {
public enum Settings {
/// Push Notifications
public static let title = NotificationsLocalization.tr("Localizable", "NOTIFICATIONS.SETTINGS.TITLE", fallback: "Push Notifications")
}
public enum Settings {
/// Notifications for course discussions you post, comment, or follow.
public static let preferenceDescription = NotificationsLocalization.tr("Localizable", "SETTINGS.PREFERENCE_DESCRIPTION", fallback: "Notifications for course discussions you post, comment, or follow.")
/// Discussions Activity
public static let preferenceTitle = NotificationsLocalization.tr("Localizable", "SETTINGS.PREFERENCE_TITLE", fallback: "Discussions Activity")
/// Push Notifications
public static let title = NotificationsLocalization.tr("Localizable", "SETTINGS.TITLE", fallback: "Push Notifications")
}
}
// swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length
Expand Down
6 changes: 5 additions & 1 deletion Notifications/Notifications/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@

*/

NOTIFICATIONS.SETTINGS.TITLE="Push Notifications";
SETTINGS.TITLE="Push Notifications";
SETTINGS.PREFERENCE_TITLE="Discussions Activity";
SETTINGS.PREFERENCE_DESCRIPTION="Notifications for course discussions you post, comment, or follow.";


4 changes: 3 additions & 1 deletion Notifications/Notifications/uk.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,6 @@

*/

NOTIFICATIONS.SETTINGS.TITLE="Push Notifications";
SETTINGS.TITLE="Push Notifications";
SETTINGS.PREFERENCE_TITLE="Discussions Activity";
SETTINGS.PREFERENCE_DESCRIPTION="Notifications for course discussions you post, comment, or follow.";
Loading

0 comments on commit 1cec44b

Please sign in to comment.