Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[MOB-2759] Onboarding card NTP Experiment #778

Merged
merged 37 commits into from
Sep 26, 2024
Merged
Show file tree
Hide file tree
Changes from 36 commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
f603f12
Create OnboardingCardNTPExperiment
lucaschifino Sep 5, 2024
bd7565c
Create NTPOnboardingCardCell
lucaschifino Sep 5, 2024
46241de
Adjust onboarding card UI
lucaschifino Sep 6, 2024
2ae78df
Adjust onboarding card constraints
lucaschifino Sep 6, 2024
dd11dea
Using correct translations
lucaschifino Sep 6, 2024
5efc188
Temporarily point to ios-core feature branch
lucaschifino Sep 6, 2024
a66a32b
Use actual Unleash flag
lucaschifino Sep 6, 2024
cec5666
Not present onboarding if on experiment
lucaschifino Sep 9, 2024
3745974
Remove NTP tooltip if on experiment
lucaschifino Sep 9, 2024
bd958f8
Add TODO
lucaschifino Sep 9, 2024
9028cdc
Add card dismissed user defaults
lucaschifino Sep 9, 2024
7984bcb
Set card dismissed
lucaschifino Sep 9, 2024
491f63b
Show onboarding on card click
lucaschifino Sep 9, 2024
dee519f
Add TODO
lucaschifino Sep 9, 2024
027e439
Change onboarding analytics category when on experiment
lucaschifino Sep 9, 2024
83adb87
Hide skip button when on experiment
lucaschifino Sep 9, 2024
8b2d65a
Add Analytics tracking
lucaschifino Sep 9, 2024
9c00e0b
Add abb test context to resume and launch.
lucaschifino Sep 9, 2024
7a046e7
Make MockBrowserCoordinator conform to updated protocol
lucaschifino Sep 9, 2024
1ffd26c
Fix LaunchCoordinator intro tests
lucaschifino Sep 9, 2024
841c19f
Fix Ecosia ViewModel and Tooltip tests
lucaschifino Sep 9, 2024
5541802
UI updates to Onboarding Card Cell. Fixed autoresizing
d4r1091 Sep 16, 2024
e34f10b
Fix order of ViewModels
d4r1091 Sep 17, 2024
0db6958
Update SPM
d4r1091 Sep 17, 2024
1ae7800
Fix button color in onboarding card
d4r1091 Sep 17, 2024
50f8f32
Remove custom bottom spacing for better UI look
d4r1091 Sep 17, 2024
dec02b4
SPM Update
d4r1091 Sep 17, 2024
f57e4af
Add card prioritization mechanism.
d4r1091 Sep 18, 2024
ce08512
Revert tests
d4r1091 Sep 18, 2024
a0031a3
Update after rebase
d4r1091 Sep 23, 2024
808e637
Review NTPOnboardingCardCell to add the image
d4r1091 Sep 23, 2024
57b0f5b
Made Nudge Cards configurable
d4r1091 Sep 24, 2024
ac9ab67
Add accessibility to the configurable nudge card action button
d4r1091 Sep 24, 2024
4c1e311
Update on image handling
d4r1091 Sep 24, 2024
fe78731
SPM Update
d4r1091 Sep 24, 2024
18e7623
Review structure readding a dedicated view model
d4r1091 Sep 24, 2024
c501aa5
Update `forceSkipExperiment` in `skipExperiment`
d4r1091 Sep 26, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions Client.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
0BB5B30B1AC0AD1F0052877D /* LoginsHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BB5B30A1AC0AD1F0052877D /* LoginsHelper.swift */; };
0BF0DB941A8545800039F300 /* URLBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BF0DB931A8545800039F300 /* URLBarView.swift */; };
0BF1B7E31AC60DEA00A7B407 /* InsetButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0BF1B7E21AC60DEA00A7B407 /* InsetButton.swift */; };
12187B502C89B43D00BA88CA /* OnboardingCardNTPExperiment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12187B4F2C89B43D00BA88CA /* OnboardingCardNTPExperiment.swift */; };
12187B532C89E15000BA88CA /* NTPOnboardingCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 12187B522C89E15000BA88CA /* NTPOnboardingCardCell.swift */; };
158241282820698B00956B39 /* RustRemoteTabsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 158241272820698B00956B39 /* RustRemoteTabsTests.swift */; };
15DE98FD27FCED4F00F1ECDB /* RustRemoteTabs.swift in Sources */ = {isa = PBXBuildFile; fileRef = 15DE98FC27FCED4F00F1ECDB /* RustRemoteTabs.swift */; };
1B3D99F1270E89D0006E1264 /* Telemetry in Frameworks */ = {isa = PBXBuildFile; productRef = 1B3D99F0270E89D0006E1264 /* Telemetry */; };
Expand Down Expand Up @@ -367,6 +369,9 @@
2C872A622B8CD7E000B318A0 /* VersionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CE294702B7FC5A6006C22B2 /* VersionTests.swift */; };
2C872A632B8CD7E000B318A0 /* WhatsNewLocalDataProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CE2946B2B7FC5A5006C22B2 /* WhatsNewLocalDataProviderTests.swift */; };
2C872A642B8CD7E000B318A0 /* EcosiaHomeViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C872A562B8CD65100B318A0 /* EcosiaHomeViewModelTests.swift */; };
2CA995282CA2C06A001064CC /* NTPConfigurableNudgeCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CA995272CA2C06A001064CC /* NTPConfigurableNudgeCardCell.swift */; };
2CA9952A2CA2C0BB001064CC /* NTPConfigurableNudgeCardCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CA995292CA2C0BB001064CC /* NTPConfigurableNudgeCardCellViewModel.swift */; };
2CA9952E2CA2EFAA001064CC /* NTPOnboardingCardViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CA9952D2CA2EFAA001064CC /* NTPOnboardingCardViewModel.swift */; };
2CABD7162C11C9CC00A0750F /* MozillaAppServices in Frameworks */ = {isa = PBXBuildFile; productRef = 2CABD7152C11C9CC00A0750F /* MozillaAppServices */; };
2CABD7282C12EF1E00A0750F /* PrivateModeButtonTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CABD7272C12EF1E00A0750F /* PrivateModeButtonTests.swift */; };
2CC8AC342C4F887D000A669A /* MockAnalytics.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CC8AC332C4F887D000A669A /* MockAnalytics.swift */; };
Expand Down Expand Up @@ -2003,6 +2008,8 @@
10CD44F0A402C84BB31E5474 /* gu-IN */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "gu-IN"; path = "gu-IN.lproj/Intro.strings"; sourceTree = "<group>"; };
11F747589EB8A55A47647C93 /* bn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = bn; path = bn.lproj/ClearPrivateData.strings; sourceTree = "<group>"; };
120F42119EB30F217AB9493E /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/FindInPage.strings; sourceTree = "<group>"; };
12187B4F2C89B43D00BA88CA /* OnboardingCardNTPExperiment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnboardingCardNTPExperiment.swift; sourceTree = "<group>"; };
12187B522C89E15000BA88CA /* NTPOnboardingCardCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPOnboardingCardCell.swift; sourceTree = "<group>"; };
123045959E0F295753B4B4DB /* lo */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = lo; path = lo.lproj/Today.strings; sourceTree = "<group>"; };
12674A038346A46589A0AC0B /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = "el.lproj/Default Browser.strings"; sourceTree = "<group>"; };
126A40A4A5AFDFD655B0FDF4 /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = ca.lproj/ClearHistoryConfirm.strings; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2502,6 +2509,9 @@
2C9144B0B15218D8A0FCD538 /* es-AR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "es-AR"; path = "es-AR.lproj/ClearPrivateData.strings"; sourceTree = "<group>"; };
2C97EC701E72C80E0092EC18 /* TopTabsTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TopTabsTest.swift; sourceTree = "<group>"; };
2CA16FDD1E5F089100332277 /* SearchTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SearchTest.swift; sourceTree = "<group>"; };
2CA995272CA2C06A001064CC /* NTPConfigurableNudgeCardCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPConfigurableNudgeCardCell.swift; sourceTree = "<group>"; };
2CA995292CA2C0BB001064CC /* NTPConfigurableNudgeCardCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPConfigurableNudgeCardCellViewModel.swift; sourceTree = "<group>"; };
2CA9952D2CA2EFAA001064CC /* NTPOnboardingCardViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NTPOnboardingCardViewModel.swift; sourceTree = "<group>"; };
2CABD7272C12EF1E00A0750F /* PrivateModeButtonTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivateModeButtonTests.swift; sourceTree = "<group>"; };
2CAE4511992E91A32AB7D7C7 /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/Today.strings; sourceTree = "<group>"; };
2CB1728B2C61336D008551E2 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX14.5.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; };
Expand Down Expand Up @@ -7928,6 +7938,15 @@
path = SearchQuickLinksMedium;
sourceTree = "<group>";
};
12187B512C89D7C900BA88CA /* OnboardingCard */ = {
isa = PBXGroup;
children = (
12187B522C89E15000BA88CA /* NTPOnboardingCardCell.swift */,
2CA9952D2CA2EFAA001064CC /* NTPOnboardingCardViewModel.swift */,
);
path = OnboardingCard;
sourceTree = "<group>";
};
1D7B78952ADF324E0011E9F2 /* Event Queue */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -8457,6 +8476,7 @@
2C61889D2B7A8A21006B70D7 /* NTP */ = {
isa = PBXGroup;
children = (
2CA995262CA2C053001064CC /* NudgeCards */,
2C61889E2B7A8A21006B70D7 /* NTPLayout.swift */,
2C61889F2B7A8A21006B70D7 /* DefaultBrowser.swift */,
2C6188A02B7A8A21006B70D7 /* Customization */,
Expand Down Expand Up @@ -8654,6 +8674,7 @@
2C4414412BD7B43F00249464 /* BingDistributionExperiment.swift */,
2C6188F72B7A8A22006B70D7 /* EngineShortcutsExperiment.swift */,
2C6188F82B7A8A22006B70D7 /* EngagementServiceExperiment.swift */,
12187B4F2C89B43D00BA88CA /* OnboardingCardNTPExperiment.swift */,
);
path = Unleash;
sourceTree = "<group>";
Expand Down Expand Up @@ -8831,6 +8852,16 @@
path = Mocks;
sourceTree = "<group>";
};
2CA995262CA2C053001064CC /* NudgeCards */ = {
isa = PBXGroup;
children = (
12187B512C89D7C900BA88CA /* OnboardingCard */,
2CA995272CA2C06A001064CC /* NTPConfigurableNudgeCardCell.swift */,
2CA995292CA2C0BB001064CC /* NTPConfigurableNudgeCardCellViewModel.swift */,
);
path = NudgeCards;
sourceTree = "<group>";
};
2CABD71A2C11E07300A0750F /* PersistedGenerated */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -13919,10 +13950,12 @@
2C6189382B7A8A22006B70D7 /* EmptyBookmarksViewDelegate.swift in Sources */,
C8163851268A0899004C7160 /* AddCredentialViewController.swift in Sources */,
2C61898E2B7A8A22006B70D7 /* UIView+maskedCorners.swift in Sources */,
12187B502C89B43D00BA88CA /* OnboardingCardNTPExperiment.swift in Sources */,
2C61895F2B7A8A22006B70D7 /* ClimateImpactInfo.swift in Sources */,
2C6189612B7A8A22006B70D7 /* NTPAboutEcosiaCellViewModel.swift in Sources */,
8A19ACB22A3290AE001C2147 /* ClearPrivateDataSetting.swift in Sources */,
CA520E7A24913C1B00CCAB48 /* PasswordManagerViewModel.swift in Sources */,
2CA9952A2CA2C0BB001064CC /* NTPConfigurableNudgeCardCellViewModel.swift in Sources */,
8AE1E1CD27B191110024C45E /* SearchBarSettingsViewModel.swift in Sources */,
43D16B8529831EA5009F8279 /* Style.swift in Sources */,
E16258EF2A83BE0800522742 /* FakespotLoadingView.swift in Sources */,
Expand Down Expand Up @@ -13999,6 +14032,7 @@
C81A8F2526D3ED1900EBA539 /* UIWindow+Extension.swift in Sources */,
EBC4869E2195F58300CDA48D /* AboutHomeHandler.swift in Sources */,
DDA24A431FD84D630098F159 /* DefaultSearchPrefs.swift in Sources */,
12187B532C89E15000BA88CA /* NTPOnboardingCardCell.swift in Sources */,
E65075611E37F77D006961AC /* MenuHelper.swift in Sources */,
8A7A26E529D4C0A800EA76F1 /* IntroScreenManager.swift in Sources */,
8AB8574827D97CD40075C173 /* HomePanelType.swift in Sources */,
Expand Down Expand Up @@ -14291,6 +14325,7 @@
C82F4C2B29AE2DF1005BD116 /* NotificationsSettingsViewController.swift in Sources */,
2C6189712B7A8A22006B70D7 /* WelcomeTourGreen.swift in Sources */,
59A68B280D62462B85CF57A4 /* HistoryPanel.swift in Sources */,
2CA995282CA2C06A001064CC /* NTPConfigurableNudgeCardCell.swift in Sources */,
C400467C1CF4E43E00B08303 /* BackForwardListViewController.swift in Sources */,
D5D237782640BBA600326204 /* ExperimentsSettingsViewController.swift in Sources */,
D3972BF31C22412B00035B87 /* ShareExtensionHelper.swift in Sources */,
Expand All @@ -14306,6 +14341,7 @@
BD4B2DE429BB4D9A005FAA50 /* TimerSnackBar.swift in Sources */,
810FF3582B1784E7009F062C /* PrivateModeAction.swift in Sources */,
21EA466A2B04130500AAAB2D /* TabsPanelState.swift in Sources */,
2CA9952E2CA2EFAA001064CC /* NTPOnboardingCardViewModel.swift in Sources */,
D04D1B862097859B0074B35F /* DownloadToast.swift in Sources */,
C29B64EE2AD937D500F3244B /* QRCodeNavigationHandler.swift in Sources */,
8A19ACB42A3290D9001C2147 /* ContentBlockerSetting.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/braze-inc/braze-swift-sdk",
"state" : {
"revision" : "d54bd0676c6dcc630793f1c4e3be53bb5b51b67e",
"version" : "10.2.0"
"revision" : "f6b0226e04d19bb79f7fa57cf9f1aa56abe465ff",
"version" : "10.3.1"
}
},
{
Expand Down Expand Up @@ -69,7 +69,7 @@
"location" : "https://github.com/ecosia/ios-core.git",
"state" : {
"branch" : "main",
"revision" : "89180b4f06dc454bbba64d0ff809894ac827edc2"
"revision" : "956f7bacd4c03723079aea6f66cd1d5e552ebce3"
}
},
{
Expand Down Expand Up @@ -158,17 +158,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/pointfreeco/swift-snapshot-testing",
"state" : {
"revision" : "6d932a79e7173b275b96c600c86c603cf84f153c",
"version" : "1.17.4"
"revision" : "7b0bbbae90c41f848f90ac7b4df6c4f50068256d",
"version" : "1.17.5"
}
},
{
"identity" : "swift-syntax",
"kind" : "remoteSourceControl",
"location" : "https://github.com/swiftlang/swift-syntax",
"state" : {
"revision" : "515f79b522918f83483068d99c68daeb5116342d",
"version" : "600.0.0-prerelease-2024-08-20"
"revision" : "cb53fa1bd3219b0b23ded7dfdd3b2baff266fd25",
"version" : "600.0.0"
}
},
{
Expand Down
10 changes: 8 additions & 2 deletions Client/Coordinators/Browser/BrowserCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -279,9 +279,10 @@ class BrowserCoordinator: BaseCoordinator,
browserViewController.presentIntroViewController()
}

private func showIntroOnboarding() {
// Ecosia: Add `forceSkipExperiment` - used for `OnboardingCardNTPExperiment`
private func showIntroOnboarding(forceSkipExperiment: Bool = false) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a minor comment: skipExperiment already says what the method should do, no real reason to use force, or is there? ⚔️

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The real reason behind it. I guess it was added mostly to advocate the imperative instruction. Can update 👍

let introManager = IntroScreenManager(prefs: profile.prefs)
let launchType = LaunchType.intro(manager: introManager)
let launchType = LaunchType.intro(manager: introManager, checkExperiment: !forceSkipExperiment)
startLaunch(with: launchType)
}

Expand Down Expand Up @@ -583,6 +584,11 @@ class BrowserCoordinator: BaseCoordinator,

router.present(navigationController)
}

// Ecosia: Used for `OnboardingCardNTPExperiment`
func showOnboarding() {
showIntroOnboarding(forceSkipExperiment: true)
}

// MARK: - ParentCoordinatorDelegate
func didFinish(from childCoordinator: Coordinator) {
Expand Down
4 changes: 4 additions & 0 deletions Client/Coordinators/Browser/BrowserNavigationHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ protocol BrowserNavigationHandler: AnyObject, QRCodeNavigationHandler {

/// Shows the Tab Tray View Controller.
func showTabTray(selectedPanel: TabTrayPanelType)

// Ecosia: Used for `OnboardingCardNTPExperiment`
/// Present Onboarding.
func showOnboarding()
}

extension BrowserNavigationHandler {
Expand Down
21 changes: 20 additions & 1 deletion Client/Coordinators/Launch/LaunchCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,27 @@ class LaunchCoordinator: BaseCoordinator,
func start(with launchType: LaunchType) {
let isFullScreen = launchType.isFullScreenAvailable(isIphone: isIphone)
switch launchType {
case .intro(let manager):
/* Ecosia: Change to support `OnboardingCardNTPExperiment` conditions
case .intro(let manager):
presentIntroOnboarding(with: manager, isFullScreen: isFullScreen)
*/
case .intro(let manager, let checkExperiment):
guard checkExperiment else {
presentIntroOnboarding(with: manager, isFullScreen: isFullScreen)
return
}
// TODO: Refactor `FeatureManagement.fetchConfiguration()` pre-condition - maybe a notification from FeatureManagement?
Task {
await FeatureManagement.fetchConfiguration()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering, what's seen on screen while this is being executed? Slightly relates to Luca's comment from line 52, but I would expect that at this point we have fetched the Unleash configuration already, so that we can make a synchronous decision.

Don't we still have a LoadingScreen that's used when we get an invite code? We could bundle all async start up tasks there.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with all the above and YES, it does require a refactor as we are all far from being happy about it 😅 .
When I was shown that, it already sparked some thoughts about a possible refactor immediately but then, eventually, I decided to leave it as is to speed up the data gathering.
I replied to Luca's handover comment about that, deciding eventually to leave it as is.

I tackled next steps already, creating a DRAFT RFC where I'm collecting info regarding the improvement.
If you are OK with that too, I would be more than happy to present some of the improvements I thought about when will publish the RFC.

DispatchQueue.main.async { [weak self] in
guard let self = self else { return }
guard !OnboardingCardNTPExperiment.isEnabled else {
self.parentCoordinator?.didFinishLaunch(from: self)
return
}
self.presentIntroOnboarding(with: manager, isFullScreen: isFullScreen)
}
}
Comment on lines +41 to +52
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now this is working, but I don't think it looks great as it refreshes Unleash one extra time and mixes two concurrency patterns.

Maybe this could be some sort of Notification sent by FeatureManagement whenever it is finished so that places in the code that depend on it can use to make sure it is initialised.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll need to think about it thoroughly. For the time being, given the experiment context, I thought about leaving it as is. As a next step, I made a DRAFT RFC in our Confluence page that will be document with a proposed solution for us to comment on and eventually apply.

case .update(let viewModel):
presentUpdateOnboarding(with: viewModel, isFullScreen: isFullScreen)
case .defaultBrowser:
Expand Down
3 changes: 2 additions & 1 deletion Client/Coordinators/Launch/LaunchType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ enum LaunchCoordinatorType {
}

enum LaunchType {
// Ecosia: Add `checkExperiment` - used for `OnboardingCardNTPExperiment`
/// Showing the intro onboarding
case intro(manager: IntroScreenManager)
case intro(manager: IntroScreenManager, checkExperiment: Bool = true)

/// Show the update onboarding
case update(viewModel: UpdateViewModel)
Expand Down
3 changes: 2 additions & 1 deletion Client/Ecosia/Analytics/Analytics.Values.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ extension Analytics {
topSites = "top_sites",
impact,
news,
about
about,
onboardingCard = "onboarding_card"
}

enum Browser: String {
Expand Down
13 changes: 10 additions & 3 deletions Client/Ecosia/Analytics/Analytics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,13 @@ final class Analytics: AnalyticsProtocol {
.label(label.rawValue))
}

func ntpOnboardingCardExperiment(_ action: Action) {
track(Structured(category: Category.ntp.rawValue,
action: action.rawValue)
.label(Label.NTP.onboardingCard.rawValue)
.property(OnboardingCardNTPExperiment.analyticsProperty))
}

func navigation(_ action: Action, label: Label.Navigation) {
track(Structured(category: Category.navigation.rawValue,
action: action.rawValue)
Expand Down Expand Up @@ -306,7 +313,7 @@ final class Analytics: AnalyticsProtocol {
guard let page else {
return
}
let event = Structured(category: Category.intro.rawValue,
let event = Structured(category: OnboardingCardNTPExperiment.analyticsIntroCategory ?? Category.intro.rawValue,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Matter of taste, but I am not really fond of this hidden fallback logic which relies on explicit knowledge in an allegedly agnostic function. Maybe we can have category a function parameter, but with .intro as default value. Then the caller has to decide explicitly which category to use

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree with that. I believe that was added for the simplicity of the experiment.

action: Action.display.rawValue)
.property(page.rawValue)
.value(.init(integerLiteral: index))
Expand All @@ -317,7 +324,7 @@ final class Analytics: AnalyticsProtocol {
guard let page else {
return
}
let event = Structured(category: Category.intro.rawValue,
let event = Structured(category: OnboardingCardNTPExperiment.analyticsIntroCategory ?? Category.intro.rawValue,
action: Action.click.rawValue)
.label(label.rawValue)
.property(page.rawValue)
Expand Down Expand Up @@ -346,7 +353,7 @@ extension Analytics {
private func appendTestContextIfNeeded(_ action: Analytics.Action.Activity, _ event: Structured) {
switch action {
case .resume, .launch:
addABTestContexts(to: event, toggles: [.searchShortcuts, .bingDistribution])
addABTestContexts(to: event, toggles: [.searchShortcuts, .bingDistribution, .onboardingCardNTP])
addCookieConsentContext(to: event)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/

import Foundation
import Core

struct OnboardingCardNTPExperiment {
private enum Variant: String {
case control
case first = "test1"
case second = "test2"
}

private init() {}

static var isEnabled: Bool {
Unleash.isEnabled(.onboardingCardNTP) && variant != .control
}

static private var variant: Variant {
Variant(rawValue: Unleash.getVariant(.onboardingCardNTP).name) ?? .control
}

// MARK: Analytics
static var analyticsIntroCategory: String? {
isEnabled ? "intro_card" : nil
}

static var analyticsProperty: String? {
switch variant {
case .first:
return "first_copy"
case .second:
return "second_copy"
default:
return nil
}
}

/// Send onboarding card view analytics event, but just the first time it's called.
static func trackExperimentImpression() {
let trackExperimentImpressionKey = "onboardingCardNTPExperimentImpression"
guard !UserDefaults.standard.bool(forKey: trackExperimentImpressionKey) else {
return
}
Analytics.shared.ntpOnboardingCardExperiment(.view)
UserDefaults.standard.setValue(true, forKey: trackExperimentImpressionKey)
}

// MARK: Card dismissed
static private let cardDismissedKey = "onboardingCardNTPExperimentDismissed"

static var shouldShowCard: Bool {
isEnabled && !UserDefaults.standard.bool(forKey: cardDismissedKey)
}

static func setCardDismissed() {
UserDefaults.standard.set(true, forKey: cardDismissedKey)
}

// MARK: Texts
static var title: String {
switch variant {
case .first:
return .localized(.onboardingCardNTPExperimentTitle1)
case .second:
return .localized(.onboardingCardNTPExperimentTitle2)
default:
return ""
}
}

static var description: String {
switch variant {
case .first:
return .localized(.onboardingCardNTPExperimentDescription1)
case .second:
return .localized(.onboardingCardNTPExperimentDescription2)
default:
return ""
}
}

static var buttonTitle: String {
switch variant {
case .first:
return .localized(.onboardingCardNTPExperimentButtonText1)
case .second:
return .localized(.onboardingCardNTPExperimentButtonText2)
default:
return ""
}
}
}
Loading
Loading