diff --git a/WordPress/Classes/System/Root View/ReaderPresenter.swift b/WordPress/Classes/System/Root View/ReaderPresenter.swift index 89402d258e54..f9d8ec43a9aa 100644 --- a/WordPress/Classes/System/Root View/ReaderPresenter.swift +++ b/WordPress/Classes/System/Root View/ReaderPresenter.swift @@ -89,21 +89,26 @@ public final class ReaderPresenter: NSObject, SplitViewDisplayable { } private func configure(for selection: ReaderSidebarItem) { - switch selection { + let source = ScreenTrackingSource(ScreenID.Reader.sidebar) + + let vc: UIViewController = switch selection { case .main(let screen): - show(makeViewController(for: screen)) + makeViewController(for: screen) case .allSubscriptions: - show(makeAllSubscriptionsViewController(), isLargeTitle: true) + makeAllSubscriptionsViewController(source: source) case .subscription(let objectID): - show(makeViewController(withTopicID: objectID)) + makeViewController(withTopicID: objectID) case .list(let objectID): - show(makeViewController(withTopicID: objectID)) + makeViewController(withTopicID: objectID) case .tag(let objectID): - show(makeViewController(withTopicID: objectID)) + makeViewController(withTopicID: objectID) case .organization(let objectID): - show(makeViewController(withTopicID: objectID)) + makeViewController(withTopicID: objectID) } + vc.trackingContext.source = source + + show(vc) hideSupplementaryColumnIfNeeded() } @@ -156,12 +161,16 @@ public final class ReaderPresenter: NSObject, SplitViewDisplayable { } } - private func makeAllSubscriptionsViewController() -> UIViewController { - let view = ReaderSubscriptionsView() { [weak self] selection in + private func makeAllSubscriptionsViewController(source: ScreenTrackingSource? = nil) -> UIViewController { + let view = ReaderSubscriptionsView { [weak self] selection in let streamVC = ReaderStreamViewController.controllerWithTopic(selection) + streamVC.trackingContext.source = ScreenTrackingSource(ScreenID.Reader.subscriptions, component: ElementID.Reader.subscriptionCell) self?.push(streamVC) - }.environment(\.managedObjectContext, viewContext) - let hostVC = UIHostingController(rootView: view) + } + let hostVC = UIHostingController(rootView: view + .environment(\.managedObjectContext, viewContext) + .environment(\.trackingContext, ScreenTrackingContext(source: source)) + ) hostVC.title = SharedStrings.Reader.subscriptions if sidebarViewModel.isCompact { hostVC.navigationItem.largeTitleDisplayMode = .never @@ -197,7 +206,7 @@ public final class ReaderPresenter: NSObject, SplitViewDisplayable { /// Shows the given view controller by either displaying it in the `.secondary` /// column (split view) or pushing to the navigation stack. - private func show(_ viewController: UIViewController, isLargeTitle: Bool = false) { + private func show(_ viewController: UIViewController) { if let splitViewController { guard !self.contentIsAlreadyDisplayed(viewController, in: splitViewController, for: .secondary) else { DDLogInfo("View controller for \(viewController.contentIdentifier) already presented – skipping show") @@ -206,9 +215,6 @@ public final class ReaderPresenter: NSObject, SplitViewDisplayable { (viewController as? ReaderStreamViewController)?.isNotificationsBarButtonEnabled = true let navigationVC = UINavigationController(rootViewController: viewController) - if isLargeTitle { - navigationVC.navigationBar.prefersLargeTitles = true - } splitViewController.setViewController(navigationVC, for: .secondary) } else { // Don't push a view controller on top of another with the same content diff --git a/WordPress/Classes/Utility/Analytics/AnalyticsTrackerAutomatticTracks.swift b/WordPress/Classes/Utility/Analytics/AnalyticsTrackerAutomatticTracks.swift index 9bb1ec6c8612..ffb11bcd8ab5 100644 --- a/WordPress/Classes/Utility/Analytics/AnalyticsTrackerAutomatticTracks.swift +++ b/WordPress/Classes/Utility/Analytics/AnalyticsTrackerAutomatticTracks.swift @@ -55,19 +55,26 @@ import BuildSettingsKit } public func trackString(_ event: String, withProperties properties: [AnyHashable: Any]?) { - if properties == nil { - DDLogInfo("🔵 Tracked: \(event)") - } else { - let description = (properties ?? [:]) + DDLogInfo("\(makeLogMessage(for: event, properties: properties))") + tracksService.trackEventName(event, withCustomProperties: properties) + } + + private func makeLogMessage(for event: String, properties: [AnyHashable: Any]?) -> String { + var message = "🔵 Tracked: \(event)" + if let properties, !properties.isEmpty { + if event == WPAnalyticsEvent.screenShown.value, let screen = properties["screen"] { + message.append(" >> \(screen)") + } + let description = properties .map { (key: "\($0)", value: $1) } .sorted { $0.key.localizedCaseInsensitiveCompare($1.key) == .orderedAscending } .map { key, value in "\(key): \(value)" } .joined(separator: ", ") - DDLogInfo("🔵 Tracked: \(event) <\(description)>") + message.append(" <\(description)>") } - tracksService.trackEventName(event, withCustomProperties: properties) + return message } // MARK: - Session Management diff --git a/WordPress/Classes/Utility/Analytics/ScreenTracking+IDs.swift b/WordPress/Classes/Utility/Analytics/ScreenTracking+IDs.swift new file mode 100644 index 000000000000..fb8558b5995b --- /dev/null +++ b/WordPress/Classes/Utility/Analytics/ScreenTracking+IDs.swift @@ -0,0 +1,39 @@ +import Foundation + +enum ScreenID { + /// Reader screen identifiers. + enum Reader { + static let sidebar = "reader.sidebar" + static let discover = "reader.discover" + static let following = "reader.following" + static let saved = "reader.saved" + static let likes = "reader.likes" + static let tag = "reader.tag" + static let site = "reader.site" + static let list = "reader.list" + static let organization = "reader.organization" + static let search = "reader.search" + static let article = "reader.article" + static let comments = "reader.comments" + static let subscriptions = "reader.subscriptions" + static let selectInterests = "reader.select_interests" + } +} + +enum ElementID { + /// Reader trigger component identifiers. + enum Reader { + static let postCard = "post_card" + static let postCardComment = "post_card.comment" + static let postHeaderSiteName = "post_header.site_name" + static let suggestedSitesCard = "suggested_sites_card" + static let suggestedTagsCard = "suggested_tags_card" + static let relatedPosts = "related_posts" + static let articleHeaderSiteName = "article_header.site_name" + static let tagChip = "tag_chip" + static let commentsSection = "comments_section" + static let toolbarComment = "toolbar.comment" + static let searchResult = "search_result" + static let subscriptionCell = "subscription_cell" + } +} diff --git a/WordPress/Classes/Utility/Analytics/ScreenTracking.swift b/WordPress/Classes/Utility/Analytics/ScreenTracking.swift new file mode 100644 index 000000000000..fe82834e4194 --- /dev/null +++ b/WordPress/Classes/Utility/Analytics/ScreenTracking.swift @@ -0,0 +1,94 @@ +import Foundation +import WordPressShared + +extension WPAnalytics { + /// Fires the `screen_shown` event. + static func track( + screen: String, + context: ScreenTrackingContext = ScreenTrackingContext(), + properties: [String: String] = [:] + ) { + var props = context.properties + props["screen"] = screen + props.merge(properties) { _, new in new } + WPAnalytics.track(.screenShown, properties: props) + } +} + +/// Describes the source screen and trigger that led to a navigation. +struct ScreenTrackingSource { + /// The screen the user navigated from (e.g. `"reader.discover"`). + let screen: String + + /// A sub-section within the screen (e.g. `"recommended"` for a Discover tab). + var section: String? + + /// The UI element that triggered the navigation (e.g. `"post_card"`). + var component: String? + + /// Position index for list items (0-based). + var position: Int? + + init(_ screen: String, section: String? = nil, component: String? = nil, position: Int? = nil) { + self.screen = screen + self.section = section + self.component = component + self.position = position + } +} + +/// Describes how the user arrived at the current screen. +struct ScreenTrackingContext { + var source: ScreenTrackingSource? + + /// Builds the properties dictionary for the analytics event. + var properties: [String: String] { + var props: [String: String] = [:] + if let source { + props["source"] = [source.screen, source.section, source.component] + .compactMap { $0 } + .joined(separator: ".") + props["source_screen"] = source.screen + if let section = source.section { + props["source_section"] = section + } + if let component = source.component { + props["source_component"] = component + if let position = source.position { + props["source_component_position"] = String(position) + } + } + } + return props + } +} +// MARK: - UIViewController + +import UIKit + +extension UIViewController { + /// Tracking context describing how the user arrived at this screen. + var trackingContext: ScreenTrackingContext { + get { objc_getAssociatedObject(self, &AssociatedKeys.trackingContext) as? ScreenTrackingContext ?? ScreenTrackingContext() } + set { objc_setAssociatedObject(self, &AssociatedKeys.trackingContext, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) } + } +} + +private enum AssociatedKeys { + static var trackingContext: UInt8 = 0 +} + +// MARK: - SwiftUI + +import SwiftUI + +private struct ScreenTrackingContextKey: EnvironmentKey { + static let defaultValue = ScreenTrackingContext() +} + +extension EnvironmentValues { + var trackingContext: ScreenTrackingContext { + get { self[ScreenTrackingContextKey.self] } + set { self[ScreenTrackingContextKey.self] = newValue } + } +} diff --git a/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift b/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift index f432ed4c92cd..46b133729417 100644 --- a/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift +++ b/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift @@ -3,6 +3,7 @@ import WordPressData import WordPressShared @objc public enum WPAnalyticsEvent: Int { + case screenShown case createSheetShown case createSheetActionTapped @@ -329,6 +330,7 @@ import WordPressShared // Reader: Discover case readerDiscoverChannelSelected + case readerDiscoverTabShown case readerDiscoverEditInterestsTapped // App Settings @@ -710,6 +712,8 @@ import WordPressShared /// A String that represents the event var value: String { switch self { + case .screenShown: + return "screen_shown" case .createSheetShown: return "create_sheet_shown" case .createSheetActionTapped: @@ -1282,6 +1286,8 @@ import WordPressShared // Reader: Discover case .readerDiscoverChannelSelected: return "reader_discover_channel_selected" + case .readerDiscoverTabShown: + return "reader_discover_tab_shown" case .readerDiscoverEditInterestsTapped: return "reader_discover_edit_interests_tapped" diff --git a/WordPress/Classes/ViewRelated/Reader/Cards/ReaderPostCellViewModel.swift b/WordPress/Classes/ViewRelated/Reader/Cards/ReaderPostCellViewModel.swift index 78bf556ea345..c5c8927d494b 100644 --- a/WordPress/Classes/ViewRelated/Reader/Cards/ReaderPostCellViewModel.swift +++ b/WordPress/Classes/ViewRelated/Reader/Cards/ReaderPostCellViewModel.swift @@ -89,7 +89,15 @@ final class ReaderPostCellViewModel { func showSiteDetails() { guard let viewController else { return } - ReaderHeaderAction().execute(post: post, origin: viewController) + ReaderHeaderAction().execute( + post: post, + origin: viewController, + trackingSource: viewController.resolvedSource.map { + var source = $0 + source.component = ElementID.Reader.postHeaderSiteName + return source + } + ) } func toogleBookmark() { @@ -104,7 +112,16 @@ final class ReaderPostCellViewModel { func comment() { guard let viewController else { return } - ReaderCommentAction().execute(post: post, origin: viewController, source: .postCard) + ReaderCommentAction().execute( + post: post, + origin: viewController, + source: .postCard, + trackingSource: viewController.resolvedSource.map { + var source = $0 + source.component = ElementID.Reader.postCardComment + return source + } + ) } func toggleLike() { diff --git a/WordPress/Classes/ViewRelated/Reader/Comments/ReaderCommentsViewController.swift b/WordPress/Classes/ViewRelated/Reader/Comments/ReaderCommentsViewController.swift index e1f20ba1e02f..e82e885ef14f 100644 --- a/WordPress/Classes/ViewRelated/Reader/Comments/ReaderCommentsViewController.swift +++ b/WordPress/Classes/ViewRelated/Reader/Comments/ReaderCommentsViewController.swift @@ -83,6 +83,11 @@ final class ReaderCommentsViewController: UIViewController, WPContentSyncHelperD trackCommentsOpened() } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + WPAnalytics.track(screen: ScreenID.Reader.comments, context: trackingContext) + } + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) diff --git a/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderDiscoverViewController.swift b/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderDiscoverViewController.swift index 27fad2ff6483..3a0c21a6048e 100644 --- a/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderDiscoverViewController.swift +++ b/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderDiscoverViewController.swift @@ -7,7 +7,7 @@ import WordPressShared class ReaderDiscoverViewController: UIViewController, ReaderDiscoverHeaderViewDelegate { private let headerView = ReaderDiscoverHeaderView() - private var selectedChannel: ReaderDiscoverChannel = .freshlyPresed + private var selectedChannel: ReaderDiscoverChannel = .freshlyPressed private let topic: ReaderAbstractTopic private var streamVC: ReaderStreamViewController? private weak var selectInterestsVC: ReaderSelectInterestsViewController? @@ -45,6 +45,13 @@ class ReaderDiscoverViewController: UIViewController, ReaderDiscoverHeaderViewDe showSelectInterestsIfNeeded() } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + + WPAnalytics.track(screen: ScreenID.Reader.discover, context: trackingContext) + trackDiscoverTab(selectedChannel) + } + override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) { super.traitCollectionDidChange(previousTraitCollection) @@ -83,7 +90,7 @@ class ReaderDiscoverViewController: UIViewController, ReaderDiscoverHeaderViewDe .filter { $0.slug != ReaderTagTopic.dailyPromptTag } .map(ReaderDiscoverChannel.tag) - headerView.configure(channels: [.freshlyPresed, .recommended, .latest, .firstPosts, .dailyPrompts] + channels) + headerView.configure(channels: [.freshlyPressed, .recommended, .latest, .firstPosts, .dailyPrompts] + channels) headerView.setSelectedChannel(selectedChannel) } @@ -95,7 +102,7 @@ class ReaderDiscoverViewController: UIViewController, ReaderDiscoverHeaderViewDe private func makeViewController(for channel: ReaderDiscoverChannel) -> ReaderStreamViewController { switch channel { - case .freshlyPresed: + case .freshlyPressed: ReaderStreamViewController.controllerWithTopic(ReaderHelpers.getFreshlyPressedTopic()) case .recommended: ReaderDiscoverStreamViewController(topic: topic) @@ -123,7 +130,10 @@ class ReaderDiscoverViewController: UIViewController, ReaderDiscoverHeaderViewDe // Important to set before `viewDidLoad` streamVC.isEmbeddedInDiscover = true + streamVC.suppressesScreenTracking = true streamVC.preferredTableHeaderView = headerView + streamVC.trackingContext = trackingContext + streamVC.discoverTab = selectedChannel.analyticsID addChild(streamVC) view.addSubview(streamVC.view) @@ -151,6 +161,13 @@ class ReaderDiscoverViewController: UIViewController, ReaderDiscoverHeaderViewDe self.selectedChannel = selection configureStream(for: selection) WPAnalytics.track(.readerDiscoverChannelSelected, properties: selection.analyticsProperties) + trackDiscoverTab(selection) + } + + private func trackDiscoverTab(_ channel: ReaderDiscoverChannel) { + var properties = channel.analyticsProperties + properties["source"] = "discover" + WPAnalytics.track(.readerDiscoverTabShown, properties: properties) } // MARK: Select Interests @@ -408,15 +425,25 @@ extension ReaderDiscoverStreamViewController: ReaderRecommendationsCellDelegate if topic as? ReaderTagTopic != nil { WPAnalytics.trackReader(.readerDiscoverTopicTapped) - let topicStreamViewController = ReaderStreamViewController.controllerWithTopic(topic) - navigationController?.pushViewController(topicStreamViewController, animated: true) + let controller = ReaderStreamViewController.controllerWithTopic(topic) + controller.trackingContext.source = resolvedSource.map { + var source = $0 + source.component = ElementID.Reader.suggestedTagsCard + return source + } + navigationController?.pushViewController(controller, animated: true) } else if let siteTopic = topic as? ReaderSiteTopic { var properties = [String: Any]() properties[WPAppAnalyticsKeyBlogID] = siteTopic.siteID WPAnalytics.trackReader(.readerSuggestedSiteVisited, properties: properties) - let topicStreamViewController = ReaderStreamViewController.controllerWithSiteID(siteTopic.siteID, isFeed: false) - navigationController?.pushViewController(topicStreamViewController, animated: true) + let controller = ReaderStreamViewController.controllerWithSiteID(siteTopic.siteID, isFeed: false) + controller.trackingContext.source = resolvedSource.map { + var source = $0 + source.component = ElementID.Reader.suggestedSitesCard + return source + } + navigationController?.pushViewController(controller, animated: true) } } } diff --git a/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderPostActions/ReaderCommentAction.swift b/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderPostActions/ReaderCommentAction.swift index 967857e2e7cc..01b9328ef1ae 100644 --- a/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderPostActions/ReaderCommentAction.swift +++ b/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderPostActions/ReaderCommentAction.swift @@ -7,12 +7,16 @@ final class ReaderCommentAction { post: ReaderPost, origin: UIViewController, navigateToCommentID: Int? = nil, - source: ReaderCommentsSource + source: ReaderCommentsSource, + trackingSource: ScreenTrackingSource? = nil ) { let commentsVC = ReaderCommentsViewController(post: post) commentsVC.source = source commentsVC.navigateToCommentID = navigateToCommentID as NSNumber? commentsVC.hidesBottomBarWhenPushed = true + if let trackingSource { + commentsVC.trackingContext.source = trackingSource + } if origin.traitCollection.horizontalSizeClass == .compact { let navigationVC = UINavigationController(rootViewController: commentsVC) diff --git a/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderPostActions/ReaderHeaderAction.swift b/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderPostActions/ReaderHeaderAction.swift index 6b882cf96055..ceaa556b6ce5 100644 --- a/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderPostActions/ReaderHeaderAction.swift +++ b/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderPostActions/ReaderHeaderAction.swift @@ -2,13 +2,21 @@ import UIKit import WordPressData final class ReaderHeaderAction { - func execute(post: ReaderPost, origin: UIViewController, source: ReaderStreamViewController.StatSource? = nil) { + func execute( + post: ReaderPost, + origin: UIViewController, + source: ReaderStreamViewController.StatSource? = nil, + trackingSource: ScreenTrackingSource? = nil + ) { guard let siteID = post.siteID else { return } let controller = ReaderStreamViewController.controllerWithSiteID(siteID, isFeed: post.isExternal) if let source { controller.statSource = source } + if let trackingSource { + controller.trackingContext.source = trackingSource + } origin.navigationController?.pushViewController(controller, animated: true) let properties = ReaderHelpers.statsPropertiesForPost(post, andValue: post.blogURL as AnyObject?, forKey: "url") diff --git a/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderSearchViewController.swift b/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderSearchViewController.swift index 61e6f3ae2ca3..1ccced329733 100644 --- a/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderSearchViewController.swift +++ b/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderSearchViewController.swift @@ -65,6 +65,7 @@ final class ReaderSearchViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) + WPAnalytics.track(screen: ScreenID.Reader.search, context: trackingContext) if isFirstAppearance { isFirstAppearance = false @@ -165,6 +166,8 @@ final class ReaderSearchViewController: UIViewController { return } let postSearchVC = ReaderStreamViewController.controllerWithTopic(topic) + postSearchVC.trackingContext.source = ScreenTrackingSource(ScreenID.Reader.search, component: ElementID.Reader.searchResult) + postSearchVC.suppressesScreenTracking = true showChild(postSearchVC) postsResulsViewContoller = postSearchVC diff --git a/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderStreamViewController.swift b/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderStreamViewController.swift index 10c73b5684d0..97c019264956 100644 --- a/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderStreamViewController.swift +++ b/WordPress/Classes/ViewRelated/Reader/Controllers/ReaderStreamViewController.swift @@ -186,9 +186,14 @@ import AutomatticTracks private var showConfirmation = true var isEmbeddedInDiscover = false + var suppressesScreenTracking = false var isNotificationsBarButtonEnabled = false var preferredTableHeaderView: UIView? + /// The active discover tab identifier (e.g. `"recommended"`), set by the + /// parent ``ReaderDiscoverViewController``. + var discoverTab: String? + var isCompact = true { didSet { guard oldValue != isCompact else { return } @@ -330,6 +335,10 @@ import AutomatticTracks override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) + if !suppressesScreenTracking, let screen = resolveReaderScreenID() { + WPAnalytics.track(screen: screen, context: trackingContext) + } + let mainContext = ContextManager.shared.mainContext NotificationCenter.default.removeObserver(self, name: NSNotification.Name.NSManagedObjectContextDidSave, object: mainContext) @@ -768,6 +777,40 @@ import AutomatticTracks ReaderHelpers.trackLoadedTopic(topic, withProperties: properties) } + /// The tracking source for this screen, including the discover tab section if applicable. + var resolvedSource: ScreenTrackingSource? { + guard let screen = resolveReaderScreenID() else { return nil } + var source = ScreenTrackingSource(screen) + source.section = discoverTab + return source + } + + /// Maps the current topic/content type to a `ReaderScreen` identifier. + private func resolveReaderScreenID() -> String? { + if contentType == .saved { + return ScreenID.Reader.saved + } + guard let topic = readerTopic else { + return nil + } + if ReaderHelpers.topicIsDiscover(topic) { + return ScreenID.Reader.discover + } else if ReaderHelpers.topicIsFollowing(topic) { + return ScreenID.Reader.following + } else if ReaderHelpers.topicIsLiked(topic) { + return ScreenID.Reader.likes + } else if ReaderHelpers.isTopicSite(topic) { + return ScreenID.Reader.site + } else if ReaderHelpers.isTopicTag(topic) { + return ScreenID.Reader.tag + } else if ReaderHelpers.isTopicList(topic) { + return ScreenID.Reader.list + } else if topic is ReaderTeamTopic { + return ScreenID.Reader.organization + } + return nil + } + // MARK: - Sync Methods /// Updates the last synced date for a topic. Since its possible for a sync @@ -1438,6 +1481,12 @@ extension ReaderStreamViewController: WPTableViewHandlerDelegate { let controller = ReaderDetailViewController.controllerWithPost(post) controller.coordinator?.readerTopic = readerTopic + if var source = resolvedSource { + source.component = ElementID.Reader.postCard + source.position = indexPath.row + controller.trackingContext.source = source + } + if post.isSavedForLater || contentType == .saved { trackSavedPostNavigation() } else { diff --git a/WordPress/Classes/ViewRelated/Reader/Detail/ReaderDetailCoordinator.swift b/WordPress/Classes/ViewRelated/Reader/Detail/ReaderDetailCoordinator.swift index cfeac6191f3b..23a276decdcf 100644 --- a/WordPress/Classes/ViewRelated/Reader/Detail/ReaderDetailCoordinator.swift +++ b/WordPress/Classes/ViewRelated/Reader/Detail/ReaderDetailCoordinator.swift @@ -267,6 +267,7 @@ class ReaderDetailCoordinator { if let blogID = sourceAttribution.blogID { let controller = ReaderStreamViewController.controllerWithSiteID(blogID, isFeed: false) + controller.trackingContext.source = ScreenTrackingSource(ScreenID.Reader.article, component: ElementID.Reader.articleHeaderSiteName) viewController?.navigationController?.pushViewController(controller, animated: true) return } @@ -428,6 +429,7 @@ class ReaderDetailCoordinator { } let controller = ReaderStreamViewController.controllerWithSiteID(siteID, isFeed: post.isExternal) + controller.trackingContext.source = ScreenTrackingSource(ScreenID.Reader.article, component: ElementID.Reader.articleHeaderSiteName) viewController?.navigationController?.pushViewController(controller, animated: true) let properties = ReaderHelpers.statsPropertiesForPost(post, andValue: post.blogURL as AnyObject?, forKey: "URL") @@ -436,6 +438,7 @@ class ReaderDetailCoordinator { private func showTopic(_ topic: String) { let controller = ReaderStreamViewController.controllerWithTagSlug(topic) + controller.trackingContext.source = ScreenTrackingSource(ScreenID.Reader.article, component: ElementID.Reader.tagChip) viewController?.navigationController?.pushViewController(controller, animated: true) } @@ -452,6 +455,7 @@ class ReaderDetailCoordinator { } let controller = ReaderStreamViewController.controllerWithTagSlug(primaryTagSlug) + controller.trackingContext.source = ScreenTrackingSource(ScreenID.Reader.article, component: ElementID.Reader.tagChip) viewController?.navigationController?.pushViewController(controller, animated: true) let properties = ReaderHelpers.statsPropertiesForPost(post, andValue: post.primaryTagSlug as AnyObject?, forKey: "tag") diff --git a/WordPress/Classes/ViewRelated/Reader/Detail/ReaderDetailViewController.swift b/WordPress/Classes/ViewRelated/Reader/Detail/ReaderDetailViewController.swift index 39448ef9b695..2aa455c36796 100644 --- a/WordPress/Classes/ViewRelated/Reader/Detail/ReaderDetailViewController.swift +++ b/WordPress/Classes/ViewRelated/Reader/Detail/ReaderDetailViewController.swift @@ -240,6 +240,11 @@ class ReaderDetailViewController: UIViewController, ReaderDetailView { } } + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + WPAnalytics.track(screen: ScreenID.Reader.article, context: trackingContext) + } + override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) @@ -369,7 +374,8 @@ class ReaderDetailViewController: UIViewController, ReaderDetailView { post: post, origin: self, navigateToCommentID: commentID, - source: .postDetails + source: .postDetails, + trackingSource: ScreenTrackingSource(ScreenID.Reader.article, component: ElementID.Reader.commentsSection) ) } } @@ -1165,6 +1171,7 @@ extension ReaderDetailViewController: UITableViewDataSource, UITableViewDelegate guard let controller = ReaderDetailViewController.controllerWithSimplePost(post) else { return } + controller.trackingContext.source = ScreenTrackingSource(ScreenID.Reader.article, component: ElementID.Reader.relatedPosts, position: indexPath.row) navigationController?.pushViewController(controller, animated: true) } @@ -1464,7 +1471,8 @@ extension ReaderDetailViewController: BorderedButtonTableViewCellDelegate { ReaderCommentAction().execute( post: post, origin: self, - source: .postDetailsComments + source: .postDetailsComments, + trackingSource: ScreenTrackingSource(ScreenID.Reader.article, component: ElementID.Reader.commentsSection) ) } diff --git a/WordPress/Classes/ViewRelated/Reader/Detail/Views/ReaderDetailToolbar.swift b/WordPress/Classes/ViewRelated/Reader/Detail/Views/ReaderDetailToolbar.swift index 3c71c486b655..189c301adc06 100644 --- a/WordPress/Classes/ViewRelated/Reader/Detail/Views/ReaderDetailToolbar.swift +++ b/WordPress/Classes/ViewRelated/Reader/Detail/Views/ReaderDetailToolbar.swift @@ -208,7 +208,7 @@ class ReaderDetailToolbar { return } - ReaderCommentAction().execute(post: post, origin: viewController, source: .postDetails) + ReaderCommentAction().execute(post: post, origin: viewController, source: .postDetails, trackingSource: ScreenTrackingSource(ScreenID.Reader.article, component: ElementID.Reader.toolbarComment)) } @objc private func didTapLike(_ sender: Any) { diff --git a/WordPress/Classes/ViewRelated/Reader/Headers/ReaderDiscoverHeaderView.swift b/WordPress/Classes/ViewRelated/Reader/Headers/ReaderDiscoverHeaderView.swift index 007c6caf17a2..da0bc38a870e 100644 --- a/WordPress/Classes/ViewRelated/Reader/Headers/ReaderDiscoverHeaderView.swift +++ b/WordPress/Classes/ViewRelated/Reader/Headers/ReaderDiscoverHeaderView.swift @@ -167,7 +167,7 @@ private final class ReaderDiscoverChannelView: UIView { enum ReaderDiscoverChannel: Hashable { /// Curated posts. - case freshlyPresed + case freshlyPressed /// The default channel showing your selected tags. case recommended @@ -185,7 +185,7 @@ enum ReaderDiscoverChannel: Hashable { var localizedTitle: String { switch self { - case .freshlyPresed: + case .freshlyPressed: NSLocalizedString("reader.discover.channel.freshlyPresed", value: "Freshly Pressed", comment: "Header view channel (filter)") case .recommended: NSLocalizedString("reader.discover.channel.forYou", value: "For You", comment: "Header view channel (filter)") @@ -208,9 +208,9 @@ enum ReaderDiscoverChannel: Hashable { return properties } - private var analyticsID: String { + var analyticsID: String { switch self { - case .freshlyPresed: "freshly_presed" + case .freshlyPressed: "freshly_pressed" case .recommended: "recommended" case .firstPosts: "first_posts" case .latest: "latest" @@ -218,6 +218,7 @@ enum ReaderDiscoverChannel: Hashable { case .tag: "tag" } } + } #Preview { diff --git a/WordPress/Classes/ViewRelated/Reader/Subscriptions/ReaderSubscriptionsView.swift b/WordPress/Classes/ViewRelated/Reader/Subscriptions/ReaderSubscriptionsView.swift index d783bf2dbe52..282444da3de7 100644 --- a/WordPress/Classes/ViewRelated/Reader/Subscriptions/ReaderSubscriptionsView.swift +++ b/WordPress/Classes/ViewRelated/Reader/Subscriptions/ReaderSubscriptionsView.swift @@ -22,6 +22,7 @@ struct ReaderSubscriptionsView: View { @Environment(\.horizontalSizeClass) private var horizontalSizeClass + @Environment(\.trackingContext) private var trackingContext var onSelection: (_ subscription: ReaderSiteTopic) -> Void = { _ in } var body: some View { @@ -36,6 +37,9 @@ struct ReaderSubscriptionsView: View { main } } + .onAppear { + WPAnalytics.track(screen: ScreenID.Reader.subscriptions, context: trackingContext) + } .refreshable { await viewModel.refresh() }