Skip to content

Commit

Permalink
update ui background and add some unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
brindy committed Aug 1, 2023
1 parent 0c4e410 commit 4b92d28
Show file tree
Hide file tree
Showing 4 changed files with 313 additions and 25 deletions.
14 changes: 14 additions & 0 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1387,6 +1387,8 @@
85799C1825DEBB3F0007EC87 /* Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85799C1725DEBB3F0007EC87 /* Logging.swift */; };
857E5AF52A79045800FC0FB4 /* PixelExperiment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 857E5AF42A79045800FC0FB4 /* PixelExperiment.swift */; };
857E5AF62A790B7000FC0FB4 /* PixelExperiment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 857E5AF42A79045800FC0FB4 /* PixelExperiment.swift */; };
857E5AFA2A7961FF00FC0FB4 /* PixelExperimentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 857E5AF82A79618100FC0FB4 /* PixelExperimentTests.swift */; };
857E5AFB2A79628A00FC0FB4 /* PixelExperimentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 857E5AF82A79618100FC0FB4 /* PixelExperimentTests.swift */; };
857FFEC027D239DC00415E7A /* HyperLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 857FFEBF27D239DC00415E7A /* HyperLink.swift */; };
8589063A267BCD8E00D23B0D /* SaveCredentialsPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85890639267BCD8E00D23B0D /* SaveCredentialsPopover.swift */; };
8589063C267BCDC000D23B0D /* SaveCredentialsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8589063B267BCDC000D23B0D /* SaveCredentialsViewController.swift */; };
Expand Down Expand Up @@ -2621,6 +2623,7 @@
85774B022A71CDD000DE0561 /* BlockMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlockMenuItem.swift; sourceTree = "<group>"; };
85799C1725DEBB3F0007EC87 /* Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Logging.swift; sourceTree = "<group>"; };
857E5AF42A79045800FC0FB4 /* PixelExperiment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PixelExperiment.swift; sourceTree = "<group>"; };
857E5AF82A79618100FC0FB4 /* PixelExperimentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PixelExperimentTests.swift; sourceTree = "<group>"; };
857FFEBF27D239DC00415E7A /* HyperLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HyperLink.swift; sourceTree = "<group>"; };
85890639267BCD8E00D23B0D /* SaveCredentialsPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveCredentialsPopover.swift; sourceTree = "<group>"; };
8589063B267BCDC000D23B0D /* SaveCredentialsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveCredentialsViewController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4619,6 +4622,14 @@
path = Experiment;
sourceTree = "<group>";
};
857E5AF72A79617100FC0FB4 /* PixelExperiment */ = {
isa = PBXGroup;
children = (
857E5AF82A79618100FC0FB4 /* PixelExperimentTests.swift */,
);
path = PixelExperiment;
sourceTree = "<group>";
};
8585B63526D6E5F600C1416F /* SwiftUI */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -6386,6 +6397,7 @@
B6DA440F2616C0F200DD1EC2 /* Statistics */ = {
isa = PBXGroup;
children = (
857E5AF72A79617100FC0FB4 /* PixelExperiment */,
B69B50402726C3F400758A2B /* ATB */,
B6DA44102616C0FC00DD1EC2 /* PixelTests.swift */,
4BC2621C293996410087A482 /* PixelEventTests.swift */,
Expand Down Expand Up @@ -8230,6 +8242,7 @@
3706FE61293F661700E42796 /* PinnedTabsViewModelTests.swift in Sources */,
3706FE62293F661700E42796 /* PasswordManagementListSectionTests.swift in Sources */,
3706FE63293F661700E42796 /* RecentlyClosedCoordinatorMock.swift in Sources */,
857E5AFB2A79628A00FC0FB4 /* PixelExperimentTests.swift in Sources */,
3706FE64293F661700E42796 /* DownloadListStoreTests.swift in Sources */,
3706FE65293F661700E42796 /* ContentBlockingUpdatingTests.swift in Sources */,
3706FE67293F661700E42796 /* EncryptionMocks.swift in Sources */,
Expand Down Expand Up @@ -9218,6 +9231,7 @@
B60C6F7E29B1B41D007BFAA8 /* TestRunHelperInitializer.m in Sources */,
37479F152891BC8300302FE2 /* TabCollectionViewModelTests+WithoutPinnedTabsManager.swift in Sources */,
AA63745424C9BF9A00AB2AC4 /* SuggestionContainerTests.swift in Sources */,
857E5AFA2A7961FF00FC0FB4 /* PixelExperimentTests.swift in Sources */,
AAC9C01524CAFBCE00AD1325 /* TabTests.swift in Sources */,
B69B504C2726CA2900758A2B /* MockVariantManager.swift in Sources */,
310E79BF294A19A8007C49E8 /* FireproofingReferenceTests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,18 @@ struct BookmarksBarPromptView: View {
@ObservedObject var model = BookmarksBarPromptViewModel()

var body: some View {
VStack(spacing: 16) {
VStack(spacing: 0) {
Image("BookmarksBarIllustration")
.padding(.bottom, 16)

Text("Show Bookmarks Bar for quick access to your bookmarks")
.font(Font.custom("SF Pro Text", size: 15)
.weight(.semibold))
.padding(.bottom, 16)

Text("Manage Bookmarks Bar in Settings > Appearance.")
.font(Font.custom("SF Pro Text", size: 13))
.padding(.bottom, 20)

HStack {
Button {
Expand Down Expand Up @@ -123,7 +126,8 @@ struct BookmarksBarPromptView: View {
.padding(.horizontal, 16)
.padding(.top, 20)
.padding(.bottom, 16)
.frame(width: 356, height: 268)
.frame(width: 356, height: 272)
.background(Color("InterfaceBackgroundColor"))
}

}
Expand Down
84 changes: 61 additions & 23 deletions DuckDuckGo/Statistics/Experiment/PixelExperiment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,41 +18,75 @@

import Foundation

/// When `cohort` is accessed for the first time, allocate and return a cohort. Subsequently, return the same cohort.
enum PixelExperiment: String, CaseIterable {

fileprivate static let logic = PixelExperimentLogic {
Pixel.fire($0)
}

/// When `cohort` is accessed for the first time, allocate and return a cohort. Subsequently, return the same cohort.
static var cohort: PixelExperiment {
logic.cohort
}

// These are the variants. Rename or add/remove them as needed. If you change the string value
// remember to keep it clear for privacy triage.
case control
case showBookmarksBarPrompt = "variant1"

}

/// These functions contain the business logic for determining if the pixel should be fired or not.
extension PixelExperiment {

static func fireEnrollmentPixel() {
logic.fireEnrollmentPixel()
}

static func fireSearchOnDay4to8Pixel() {
logic.fireSearchOnDay4to8Pixel()
}

static func fireBookmarksBarInteractionPixel() {
logic.fireBookmarksBarInteractionPixel()
}

}

final internal class PixelExperimentLogic {

var cohort: PixelExperiment {
if let allocatedCohort,
// if the stored cohort doesn't match, allocate a new one
let cohort = Self.init(rawValue: allocatedCohort) {
// if the stored cohort doesn't match, allocate a new one
let cohort = PixelExperiment(rawValue: allocatedCohort) {
return cohort
}

// For now, just use equal distribution of all cohorts.
let cohort = allCases.randomElement()!
let cohort = PixelExperiment.showBookmarksBarPrompt // PixelExperiment.allCases.randomElement()!
allocatedCohort = cohort.rawValue
enrollmentDate = Date()
fireEnrollmentPixel()
return cohort
}

@UserDefaultsWrapper(key: .pixelExperimentCohort, defaultValue: nil)
fileprivate static var allocatedCohort: String?
var allocatedCohort: String?

@UserDefaultsWrapper(key: .pixelExperimentEnrollmentDate, defaultValue: nil)
fileprivate static var enrollmentDate: Date?
var enrollmentDate: Date?

fileprivate static var daysSinceEnrollment: Int {
private var daysSinceEnrollment: Int {
guard let enrollmentDate else { return 0 }
let diff = Date().timeIntervalSince1970 - enrollmentDate.timeIntervalSince1970
let days = Int(diff / 60 / 60 / 24)
return days
}

@UserDefaultsWrapper(key: .pixelExperimentFiredPixels, defaultValue: [])
private static var firedPixelsStorage: [String]
private var firedPixelsStorage: [String]

fileprivate static var firedPixels: Set<String> {
private var firedPixels: Set<String> {
get {
Set<String>(firedPixelsStorage)
}
Expand All @@ -62,36 +96,40 @@ enum PixelExperiment: String, CaseIterable {
}
}

// These are the variants. Rename or add/remove them as needed.
case control
case showBookmarksBarPrompt = "variant1"

}
private let fire: (Pixel.Event) -> Void

/// These functions contain the business logic for determining if the pixel should be fired or not.
extension PixelExperiment {
init(fire: @escaping (Pixel.Event) -> Void) {
self.fire = fire
}

static func fireEnrollmentPixel() {
func fireEnrollmentPixel() {
guard allocatedCohort != nil else { return }
if firedPixels.insert(Pixel.Event.bookmarksBarOnboardingEnrollment(cohort: "").name).inserted {
Pixel.fire(.bookmarksBarOnboardingEnrollment(cohort: cohort.rawValue))
fire(.bookmarksBarOnboardingEnrollment(cohort: cohort.rawValue))
}
}

static func fireSearchOnDay4to8Pixel() {
func fireSearchOnDay4to8Pixel() {
guard allocatedCohort != nil else { return }
guard 4...8 ~= daysSinceEnrollment else { return }
if firedPixels.insert(Pixel.Event.bookmarksBarOnboardingSearched4to8days(cohort: "").name).inserted {
Pixel.fire(.bookmarksBarOnboardingSearched4to8days(cohort: cohort.rawValue))
fire(.bookmarksBarOnboardingSearched4to8days(cohort: cohort.rawValue))
}
}

static func fireBookmarksBarInteractionPixel() {
func fireBookmarksBarInteractionPixel() {
guard allocatedCohort != nil else { return }
if firedPixels.insert(Pixel.Event.bookmarksBarOnboardingFirstInteraction(cohort: "").name).inserted {
Pixel.fire(.bookmarksBarOnboardingFirstInteraction(cohort: cohort.rawValue))
fire(.bookmarksBarOnboardingFirstInteraction(cohort: cohort.rawValue))
} else if 2...8 ~= daysSinceEnrollment && firedPixels.insert(Pixel.Event.bookmarksBarOnboardingInteraction2to8days(cohort: "").name).inserted {
Pixel.fire(.bookmarksBarOnboardingInteraction2to8days(cohort: cohort.rawValue))
fire(.bookmarksBarOnboardingInteraction2to8days(cohort: cohort.rawValue))
}
}

func reset() {
allocatedCohort = nil
enrollmentDate = nil
firedPixelsStorage = []
}

}
Loading

0 comments on commit 4b92d28

Please sign in to comment.