Skip to content

Commit

Permalink
adding impression pixels for duckplayer in landscape mode (#3493)
Browse files Browse the repository at this point in the history
<!--
Note: This checklist is a reminder of our shared engineering
expectations. Feel free to change it, although assigning a GitHub
reviewer and the items in bold are required.

⚠️ If you're an external contributor, please file an issue first before
working on a PR, as we can't guarantee that we will accept your changes
if they haven't been discussed ahead of time. Thanks!
-->

Task/Issue URL:
https://app.asana.com/0/1201141132935289/1208637202774549/f
Tech Design URL:
CC:

**Description**:

the DuckPlayer frontend will start sending a generic 'telemetry' events
soon - this PR adds support for the first one, but in a format that can
be expanded later without needing new handlers.
  • Loading branch information
shakyShane authored Oct 30, 2024
1 parent 82f02f0 commit 374492a
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 1 deletion.
4 changes: 4 additions & 0 deletions Core/PixelEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -793,6 +793,7 @@ extension Pixel {
case duckPlayerViewFromSERP
case duckPlayerViewFromOther
case duckPlayerOverlayYoutubeImpressions
case duckPlayerLandscapeLayoutImpressions
case duckPlayerOverlayYoutubeWatchHere
case duckPlayerSettingAlwaysDuckPlayer
case duckPlayerSettingAlwaysSettings
Expand Down Expand Up @@ -1664,6 +1665,9 @@ extension Pixel.Event {

// MARK: - WebView Error Page shown
case .webViewErrorPageShown: return "m_errorpageshown"

// MARK: - DuckPlayer FE Application Telemetry
case .duckPlayerLandscapeLayoutImpressions: return "duckplayer_landscape_layout_impressions"
}
}
}
Expand Down
64 changes: 63 additions & 1 deletion DuckDuckGo/DuckPlayer/DuckPlayer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,50 @@ public enum DuckPlayerReferrer {
}
}

// Wrapper to allow sibling properties on each event in the future.
struct TelemetryEvent: Decodable {
let attributes: Attributes
}

// This is the first example of a new telemetry event
struct ImpressionAttributes: Decodable {
enum Layout: String, Decodable {
case landscape = "landscape-layout"
}

let name: String
let value: Layout
}

// Designed to represent the discriminated union used by the FE (where all events are schema-driven)
enum Attributes: Decodable {

// more events can be added here later, without needing a new handler
case impression(ImpressionAttributes)

private enum CodingKeys: String, CodingKey {
case name
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let name = try container.decode(String.self, forKey: .name)

switch name {
case "impression":
let attributes = try ImpressionAttributes(from: decoder)
self = .impression(attributes)

default:
throw DecodingError.dataCorruptedError(
forKey: .name,
in: container,
debugDescription: "Unknown name value: \(name)"
)
}
}
}

protocol DuckPlayerProtocol: AnyObject {

var settings: DuckPlayerSettings { get }
Expand All @@ -104,7 +148,8 @@ protocol DuckPlayerProtocol: AnyObject {
func openVideoInDuckPlayer(url: URL, webView: WKWebView)
func openDuckPlayerSettings(params: Any, message: WKScriptMessage) async -> Encodable?
func openDuckPlayerInfo(params: Any, message: WKScriptMessage) async -> Encodable?

func telemetryEvent(params: Any, message: WKScriptMessage) async -> Encodable?

func initialSetupPlayer(params: Any, message: WKScriptMessage) async -> Encodable?
func initialSetupOverlay(params: Any, message: WKScriptMessage) async -> Encodable?

Expand Down Expand Up @@ -239,6 +284,23 @@ final class DuckPlayer: DuckPlayerProtocol {
return nil
}

@MainActor
public func telemetryEvent(params: Any, message: WKScriptMessage) async -> Encodable? {
guard let event: TelemetryEvent = DecodableHelper.decode(from: params) else {
return nil
}

switch event.attributes {
case .impression(let attrs):
switch attrs.value {
case .landscape:
Pixel.fire(pixel: .duckPlayerLandscapeLayoutImpressions)
}
}

return nil
}

private func encodeUserValues() -> UserValues {
return UserValues(
duckPlayerMode: featureFlagger.isFeatureOn(.duckPlayer) ? settings.mode : .disabled,
Expand Down
3 changes: 3 additions & 0 deletions DuckDuckGo/DuckPlayer/YoutubePlayerUserScript.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ final class YoutubePlayerUserScript: NSObject, Subfeature {
static let initialSetup = "initialSetup"
static let openSettings = "openSettings"
static let openInfo = "openInfo"
static let telemetryEvent = "telemetryEvent"
}

init(duckPlayer: DuckPlayerProtocol) {
Expand Down Expand Up @@ -79,6 +80,8 @@ final class YoutubePlayerUserScript: NSObject, Subfeature {
return duckPlayer.openDuckPlayerSettings
case Handlers.openInfo:
return duckPlayer.openDuckPlayerInfo
case Handlers.telemetryEvent:
return duckPlayer.telemetryEvent
default:
assertionFailure("YoutubePlayerUserScript: Failed to parse User Script message: \(methodName)")
return nil
Expand Down
5 changes: 5 additions & 0 deletions DuckDuckGoTests/DuckPlayerMocks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ final class MockDuckPlayerSettings: DuckPlayerSettings {

final class MockDuckPlayer: DuckPlayerProtocol {

func telemetryEvent(params: Any, message: WKScriptMessage) async -> (any Encodable)? {
nil
}


var hostView: UIViewController?

func openDuckPlayerSettings(params: Any, message: WKScriptMessage) async -> (any Encodable)? {
Expand Down

0 comments on commit 374492a

Please sign in to comment.