diff --git a/Core/Core/Analytics/CoreAnalytics.swift b/Core/Core/Analytics/CoreAnalytics.swift index 006b41c7d..b74ec00ca 100644 --- a/Core/Core/Analytics/CoreAnalytics.swift +++ b/Core/Core/Analytics/CoreAnalytics.swift @@ -73,9 +73,11 @@ public enum AnalyticsEvent: String { case resetPasswordClicked = "Logistration:Reset Password Clicked" case resetPasswordSuccess = "Logistration:Reset Password Success" case mainDiscoveryTabClicked = "MainDashboard:Discover" - case mainDashboardTabClicked = "MainDashboard:My Courses" - case mainProgramsTabClicked = "MainDashboard:My Programs" + case mainDashboardLearnTabClicked = "MainDashboard:Learn" case mainProfileTabClicked = "MainDashboard:Profile" + case mainProgramsTabClicked = "MainDashboard:My Programs" + case mainDashboardCoursesClicked = "Learn:My Courses" + case mainDashboardProgramsClicked = "Learn:My Programs" case discoverySearchBarClicked = "Discovery:Search Bar Clicked" case discoveryCoursesSearch = "Discovery:Courses Search" case discoveryCourseClicked = "Discovery:Course Clicked" @@ -163,7 +165,9 @@ public enum EventBIValue: String { case viewCourseClicked = "edx.bi.app.course.info" case resumeCourseClicked = "edx.bi.app.course.resume_course.clicked" case mainDiscoveryTabClicked = "edx.bi.app.main_dashboard.discover" - case mainDashboardTabClicked = "edx.bi.app.main_dashboard.my_course" + case mainDashboardLearnTabClicked = "edx.bi.app.main_dashboard.learn" + case mainDashboardCoursesClicked = "edx.bi.app.main_dashboard.learn.my_course" + case mainDashboardProgramsClicked = "edx.bi.app.main_dashboard.learn.my_programs" case mainProgramsTabClicked = "edx.bi.app.main_dashboard.my_program" case mainProfileTabClicked = "edx.bi.app.main_dashboard.profile" case profileEditClicked = "edx.bi.app.profile.edit.clicked" diff --git a/Course/Course/Presentation/Container/CourseContainerView.swift b/Course/Course/Presentation/Container/CourseContainerView.swift index 7f773561e..0043f3fe0 100644 --- a/Course/Course/Presentation/Container/CourseContainerView.swift +++ b/Course/Course/Presentation/Container/CourseContainerView.swift @@ -294,6 +294,7 @@ public struct CourseContainerView: View { Task { await viewModel.tryToRefreshCookies() } + viewModel.analytics.courseOutlineCourseTabClicked(courseId: courseID, courseName: title) } } diff --git a/Course/Course/Presentation/Handouts/HandoutsView.swift b/Course/Course/Presentation/Handouts/HandoutsView.swift index e3998963c..d04be11af 100644 --- a/Course/Course/Presentation/Handouts/HandoutsView.swift +++ b/Course/Course/Presentation/Handouts/HandoutsView.swift @@ -78,7 +78,7 @@ struct HandoutsView: View { cssInjector: viewModel.cssInjector, type: type ) - viewModel.analytics.trackCourseEvent( + viewModel.analytics.trackCourseScreenEvent( .courseAnnouncement, biValue: .courseAnnouncement, courseID: courseID diff --git a/Dashboard/Dashboard/Presentation/DashboardAnalytics.swift b/Dashboard/Dashboard/Presentation/DashboardAnalytics.swift index d5edf28d5..2dab5cf39 100644 --- a/Dashboard/Dashboard/Presentation/DashboardAnalytics.swift +++ b/Dashboard/Dashboard/Presentation/DashboardAnalytics.swift @@ -10,10 +10,14 @@ import Foundation //sourcery: AutoMockable public protocol DashboardAnalytics { func dashboardCourseClicked(courseID: String, courseName: String) + func mainProgramsClicked() + func mainCoursesClicked() } #if DEBUG class DashboardAnalyticsMock: DashboardAnalytics { public func dashboardCourseClicked(courseID: String, courseName: String) {} + public func mainProgramsClicked() {} + public func mainCoursesClicked() {} } #endif diff --git a/Dashboard/Dashboard/Presentation/Elements/DropDownMenu.swift b/Dashboard/Dashboard/Presentation/Elements/DropDownMenu.swift index beaf3bec5..3c6d678a0 100644 --- a/Dashboard/Dashboard/Presentation/Elements/DropDownMenu.swift +++ b/Dashboard/Dashboard/Presentation/Elements/DropDownMenu.swift @@ -26,6 +26,7 @@ enum MenuOption: String, CaseIterable { struct DropDownMenu: View { @Binding var selectedOption: MenuOption @State private var expanded: Bool = false + var analytics: DashboardAnalytics var body: some View { VStack(alignment: .leading, spacing: 2) { @@ -53,6 +54,12 @@ struct DropDownMenu: View { action: { selectedOption = option expanded = false + switch selectedOption { + case .courses: + analytics.mainCoursesClicked() + case .programs: + analytics.mainProgramsClicked() + } }, label: { HStack { Text(option.text) diff --git a/Dashboard/Dashboard/Presentation/PrimaryCourseDashboardView.swift b/Dashboard/Dashboard/Presentation/PrimaryCourseDashboardView.swift index e7af096b1..8b04aa91d 100644 --- a/Dashboard/Dashboard/Presentation/PrimaryCourseDashboardView.swift +++ b/Dashboard/Dashboard/Presentation/PrimaryCourseDashboardView.swift @@ -214,10 +214,6 @@ public struct PrimaryCourseDashboardView: View { id: \.offset ) { _, course in Button(action: { - viewModel.trackDashboardCourseClicked( - courseID: course.courseID, - courseName: course.name - ) router.showCourseScreens( courseID: course.courseID, hasAccess: course.hasAccess, @@ -308,7 +304,7 @@ public struct PrimaryCourseDashboardView: View { } if showDropdown { HStack(alignment: .center) { - DropDownMenu(selectedOption: $selectedMenu) + DropDownMenu(selectedOption: $selectedMenu, analytics: viewModel.analytics) Spacer() } } diff --git a/Dashboard/Dashboard/Presentation/PrimaryCourseDashboardViewModel.swift b/Dashboard/Dashboard/Presentation/PrimaryCourseDashboardViewModel.swift index 0fd052940..b064907b4 100644 --- a/Dashboard/Dashboard/Presentation/PrimaryCourseDashboardViewModel.swift +++ b/Dashboard/Dashboard/Presentation/PrimaryCourseDashboardViewModel.swift @@ -29,7 +29,7 @@ public class PrimaryCourseDashboardViewModel: ObservableObject { let connectivity: ConnectivityProtocol private let interactor: DashboardInteractorProtocol - private let analytics: DashboardAnalytics + let analytics: DashboardAnalytics let config: ConfigProtocol let storage: CoreStorage private var cancellables = Set() diff --git a/Dashboard/DashboardTests/DashboardMock.generated.swift b/Dashboard/DashboardTests/DashboardMock.generated.swift index 4fc3fb8d6..1508c25a1 100644 --- a/Dashboard/DashboardTests/DashboardMock.generated.swift +++ b/Dashboard/DashboardTests/DashboardMock.generated.swift @@ -3459,9 +3459,23 @@ open class DashboardAnalyticsMock: DashboardAnalytics, Mock { perform?(`courseID`, `courseName`) } + open func mainProgramsClicked() { + addInvocation(.m_mainProgramsClicked) + let perform = methodPerformValue(.m_mainProgramsClicked) as? () -> Void + perform?() + } + + open func mainCoursesClicked() { + addInvocation(.m_mainCoursesClicked) + let perform = methodPerformValue(.m_mainCoursesClicked) as? () -> Void + perform?() + } + fileprivate enum MethodType { case m_dashboardCourseClicked__courseID_courseIDcourseName_courseName(Parameter, Parameter) + case m_mainProgramsClicked + case m_mainCoursesClicked static func compareParameters(lhs: MethodType, rhs: MethodType, matcher: Matcher) -> Matcher.ComparisonResult { switch (lhs, rhs) { @@ -3470,17 +3484,26 @@ open class DashboardAnalyticsMock: DashboardAnalytics, Mock { results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCourseid, rhs: rhsCourseid, with: matcher), lhsCourseid, rhsCourseid, "courseID")) results.append(Matcher.ParameterComparisonResult(Parameter.compare(lhs: lhsCoursename, rhs: rhsCoursename, with: matcher), lhsCoursename, rhsCoursename, "courseName")) return Matcher.ComparisonResult(results) + + case (.m_mainProgramsClicked, .m_mainProgramsClicked): return .match + + case (.m_mainCoursesClicked, .m_mainCoursesClicked): return .match + default: return .none } } func intValue() -> Int { switch self { case let .m_dashboardCourseClicked__courseID_courseIDcourseName_courseName(p0, p1): return p0.intValue + p1.intValue + case .m_mainProgramsClicked: return 0 + case .m_mainCoursesClicked: return 0 } } func assertionName() -> String { switch self { case .m_dashboardCourseClicked__courseID_courseIDcourseName_courseName: return ".dashboardCourseClicked(courseID:courseName:)" + case .m_mainProgramsClicked: return ".mainProgramsClicked()" + case .m_mainCoursesClicked: return ".mainCoursesClicked()" } } } @@ -3500,6 +3523,8 @@ open class DashboardAnalyticsMock: DashboardAnalytics, Mock { fileprivate var method: MethodType public static func dashboardCourseClicked(courseID: Parameter, courseName: Parameter) -> Verify { return Verify(method: .m_dashboardCourseClicked__courseID_courseIDcourseName_courseName(`courseID`, `courseName`))} + public static func mainProgramsClicked() -> Verify { return Verify(method: .m_mainProgramsClicked)} + public static func mainCoursesClicked() -> Verify { return Verify(method: .m_mainCoursesClicked)} } public struct Perform { @@ -3509,6 +3534,12 @@ open class DashboardAnalyticsMock: DashboardAnalytics, Mock { public static func dashboardCourseClicked(courseID: Parameter, courseName: Parameter, perform: @escaping (String, String) -> Void) -> Perform { return Perform(method: .m_dashboardCourseClicked__courseID_courseIDcourseName_courseName(`courseID`, `courseName`), performs: perform) } + public static func mainProgramsClicked(perform: @escaping () -> Void) -> Perform { + return Perform(method: .m_mainProgramsClicked, performs: perform) + } + public static func mainCoursesClicked(perform: @escaping () -> Void) -> Perform { + return Perform(method: .m_mainCoursesClicked, performs: perform) + } } public func given(_ method: Given) { diff --git a/Discovery/Discovery/Presentation/WebDiscovery/DiscoveryWebviewViewModel.swift b/Discovery/Discovery/Presentation/WebDiscovery/DiscoveryWebviewViewModel.swift index 86f17ef5a..aed09effd 100644 --- a/Discovery/Discovery/Presentation/WebDiscovery/DiscoveryWebviewViewModel.swift +++ b/Discovery/Discovery/Presentation/WebDiscovery/DiscoveryWebviewViewModel.swift @@ -169,6 +169,7 @@ extension DiscoveryWebviewViewModel: WebViewNavigationDelegate { } case .courseDetail: guard let pathID = detailPathID(from: url) else { return false } + analytics.discoveryScreenEvent(event: .viewCourseClicked, biValue: .viewCourseClicked) router.showWebDiscoveryDetails( pathID: pathID, discoveryType: .courseDetail(pathID), diff --git a/OpenEdX/Managers/AnalyticsManager/AnalyticsManager.swift b/OpenEdX/Managers/AnalyticsManager/AnalyticsManager.swift index 2487a8c7a..5e3ca3ab9 100644 --- a/OpenEdX/Managers/AnalyticsManager/AnalyticsManager.swift +++ b/OpenEdX/Managers/AnalyticsManager/AnalyticsManager.swift @@ -146,8 +146,8 @@ class AnalyticsManager: AuthorizationAnalytics, trackScreenEvent(.mainDiscoveryTabClicked, biValue: .mainDiscoveryTabClicked) } - public func mainDashboardTabClicked() { - trackEvent(.mainDashboardTabClicked, biValue: .mainDashboardTabClicked) + public func mainLearnTabClicked() { + trackScreenEvent(.mainDashboardLearnTabClicked, biValue: .mainDashboardLearnTabClicked) } public func mainProgramsTabClicked() { @@ -158,6 +158,14 @@ class AnalyticsManager: AuthorizationAnalytics, trackScreenEvent(.mainProfileTabClicked, biValue: .mainProfileTabClicked) } + public func mainCoursesClicked() { + trackScreenEvent(.mainDashboardCoursesClicked, biValue: .mainDashboardCoursesClicked) + } + + public func mainProgramsClicked() { + trackScreenEvent(.mainDashboardProgramsClicked, biValue: .mainDashboardProgramsClicked) + } + // MARK: Discovery public func discoverySearchBarClicked() { diff --git a/OpenEdX/Managers/AnalyticsManager/MainScreenAnalytics.swift b/OpenEdX/Managers/AnalyticsManager/MainScreenAnalytics.swift index fc7bdb38c..f65a76585 100644 --- a/OpenEdX/Managers/AnalyticsManager/MainScreenAnalytics.swift +++ b/OpenEdX/Managers/AnalyticsManager/MainScreenAnalytics.swift @@ -10,16 +10,20 @@ import Foundation //sourcery: AutoMockable public protocol MainScreenAnalytics { func mainDiscoveryTabClicked() - func mainDashboardTabClicked() - func mainProgramsTabClicked() + func mainLearnTabClicked() func mainProfileTabClicked() + func mainProgramsTabClicked() + func mainCoursesClicked() + func mainProgramsClicked() } #if DEBUG public class MainScreenAnalyticsMock: MainScreenAnalytics { public func mainDiscoveryTabClicked() {} - public func mainDashboardTabClicked() {} - public func mainProgramsTabClicked() {} + public func mainLearnTabClicked() {} public func mainProfileTabClicked() {} + public func mainProgramsTabClicked() {} + public func mainProgramsClicked() {} + public func mainCoursesClicked() {} } #endif diff --git a/OpenEdX/Router.swift b/OpenEdX/Router.swift index 721dac5d3..e238568dc 100644 --- a/OpenEdX/Router.swift +++ b/OpenEdX/Router.swift @@ -387,6 +387,10 @@ public class Router: AuthorizationRouter, try? await Task.sleep(for: .seconds(1)) await Container.shared.resolve(PushNotificationsManager.self)?.performRegistration() } + + if let analytics = Container.shared.resolve(DashboardAnalytics.self) { + analytics.dashboardCourseClicked(courseID: courseID, courseName: title) + } } public func getCourseScreensController( diff --git a/OpenEdX/View/MainScreenView.swift b/OpenEdX/View/MainScreenView.swift index f430a4662..f8d4a6200 100644 --- a/OpenEdX/View/MainScreenView.swift +++ b/OpenEdX/View/MainScreenView.swift @@ -179,7 +179,7 @@ struct MainScreenView: View { case .discovery: viewModel.trackMainDiscoveryTabClicked() case .dashboard: - viewModel.trackMainDashboardTabClicked() + viewModel.trackMainDashboardLearnTabClicked() case .programs: viewModel.trackMainProgramsTabClicked() case .profile: @@ -191,6 +191,8 @@ struct MainScreenView: View { await viewModel.prefetchDataForOffline() await viewModel.loadCalendar() } + viewModel.trackMainDashboardLearnTabClicked() + viewModel.trackMainDashboardMyCoursesClicked() } .accentColor(Theme.Colors.accentXColor) } diff --git a/OpenEdX/View/MainScreenViewModel.swift b/OpenEdX/View/MainScreenViewModel.swift index 84fe435ab..8cc55f59f 100644 --- a/OpenEdX/View/MainScreenViewModel.swift +++ b/OpenEdX/View/MainScreenViewModel.swift @@ -73,12 +73,15 @@ final class MainScreenViewModel: ObservableObject { func trackMainDiscoveryTabClicked() { analytics.mainDiscoveryTabClicked() } - func trackMainDashboardTabClicked() { - analytics.mainDashboardTabClicked() + + func trackMainDashboardLearnTabClicked() { + analytics.mainLearnTabClicked() } + func trackMainProgramsTabClicked() { analytics.mainProgramsTabClicked() } + func trackMainProfileTabClicked() { analytics.mainProfileTabClicked() } @@ -114,6 +117,10 @@ final class MainScreenViewModel: ObservableObject { } } + func trackMainDashboardMyCoursesClicked() { + analytics.mainCoursesClicked() + } + @MainActor func prefetchDataForOffline() async { if await profileInteractor.getMyProfileOffline() == nil {