diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 908357c58a..b45683f7fc 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -273,10 +273,6 @@ 3199AF802C80734A003AEBDC /* TabModalManageable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3199AF6D2C80734A003AEBDC /* TabModalManageable.swift */; }; 3199AF832C80736C003AEBDC /* DuckPlayerOnboardingLocationValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3199AF812C80736B003AEBDC /* DuckPlayerOnboardingLocationValidatorTests.swift */; }; 3199AF842C80736C003AEBDC /* DuckPlayerOnboardingLocationValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3199AF812C80736B003AEBDC /* DuckPlayerOnboardingLocationValidatorTests.swift */; }; - 3199AF852C80736C003AEBDC /* DefaultDuckPlayerOnboardingDeciderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3199AF822C80736B003AEBDC /* DefaultDuckPlayerOnboardingDeciderTests.swift */; }; - 3199AF862C80736C003AEBDC /* DefaultDuckPlayerOnboardingDeciderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3199AF822C80736B003AEBDC /* DefaultDuckPlayerOnboardingDeciderTests.swift */; }; - 3199AF882C8073CB003AEBDC /* DuckPlayerOnboardingTabExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3199AF872C8073CA003AEBDC /* DuckPlayerOnboardingTabExtension.swift */; }; - 3199AF892C8073CB003AEBDC /* DuckPlayerOnboardingTabExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3199AF872C8073CA003AEBDC /* DuckPlayerOnboardingTabExtension.swift */; }; 31A2FD172BAB41C500D0E741 /* DataBrokerProtectionFeatureGatekeeperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31A2FD162BAB41C500D0E741 /* DataBrokerProtectionFeatureGatekeeperTests.swift */; }; 31A2FD182BAB43BA00D0E741 /* DataBrokerProtectionFeatureGatekeeperTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31A2FD162BAB41C500D0E741 /* DataBrokerProtectionFeatureGatekeeperTests.swift */; }; 31A3A4E32B0C115F0021063C /* DataBrokerProtection in Frameworks */ = {isa = PBXBuildFile; productRef = 31A3A4E22B0C115F0021063C /* DataBrokerProtection */; }; @@ -288,18 +284,10 @@ 31AA6B982B960BA50025014E /* DataBrokerProtectionLoginItemPixels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31AA6B962B960B870025014E /* DataBrokerProtectionLoginItemPixels.swift */; }; 31B4AF532901A4F20013585E /* NSEventExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31B4AF522901A4F20013585E /* NSEventExtension.swift */; }; 31C3CE0228EDC1E70002C24A /* CustomRoundedCornersShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31C3CE0128EDC1E70002C24A /* CustomRoundedCornersShape.swift */; }; - 31C3F9362C8636C2005556D1 /* DuckPlayerOnboardingExperimentPixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31C3F9352C8636C2005556D1 /* DuckPlayerOnboardingExperimentPixel.swift */; }; - 31C3F9372C8636C2005556D1 /* DuckPlayerOnboardingExperimentPixel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31C3F9352C8636C2005556D1 /* DuckPlayerOnboardingExperimentPixel.swift */; }; - 31C3F9392C864087005556D1 /* DuckPlayerOnboardingExperiment+Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31C3F9382C864087005556D1 /* DuckPlayerOnboardingExperiment+Logger.swift */; }; - 31C3F93A2C864087005556D1 /* DuckPlayerOnboardingExperiment+Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31C3F9382C864087005556D1 /* DuckPlayerOnboardingExperiment+Logger.swift */; }; 31C9ADE52AF0564500CEF57D /* WaitlistFeatureSetupHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31C9ADE42AF0564500CEF57D /* WaitlistFeatureSetupHandler.swift */; }; 31C9ADE62AF0564500CEF57D /* WaitlistFeatureSetupHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31C9ADE42AF0564500CEF57D /* WaitlistFeatureSetupHandler.swift */; }; 31CF3432288B0B1B0087244B /* NavigationBarBadgeAnimator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31CF3431288B0B1B0087244B /* NavigationBarBadgeAnimator.swift */; }; 31D5375C291D944100407A95 /* PasswordManagementBitwardenItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D5375B291D944100407A95 /* PasswordManagementBitwardenItemView.swift */; }; - 31D5CE632C89AAAF00BCB0D7 /* DuckPlayerExperimentDatePixelsUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D5CE622C89AAAF00BCB0D7 /* DuckPlayerExperimentDatePixelsUtilities.swift */; }; - 31D5CE642C89AAAF00BCB0D7 /* DuckPlayerExperimentDatePixelsUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D5CE622C89AAAF00BCB0D7 /* DuckPlayerExperimentDatePixelsUtilities.swift */; }; - 31D5CE662C89ABC200BCB0D7 /* DuckPlayerExperimentDatePixelsUtilitiesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D5CE652C89ABC200BCB0D7 /* DuckPlayerExperimentDatePixelsUtilitiesTests.swift */; }; - 31D5CE672C89ABC200BCB0D7 /* DuckPlayerExperimentDatePixelsUtilitiesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31D5CE652C89ABC200BCB0D7 /* DuckPlayerExperimentDatePixelsUtilitiesTests.swift */; }; 31DC2F222BD6DE6C001354EF /* DataBrokerPrerequisitesStatusVerifierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31DC2F202BD6DE65001354EF /* DataBrokerPrerequisitesStatusVerifierTests.swift */; }; 31DC2F232BD6E028001354EF /* DataBrokerPrerequisitesStatusVerifierTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31DC2F202BD6DE65001354EF /* DataBrokerPrerequisitesStatusVerifierTests.swift */; }; 31E163BA293A56F400963C10 /* BrokenSiteReportingReferenceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31E163B9293A56F400963C10 /* BrokenSiteReportingReferenceTests.swift */; }; @@ -311,8 +299,6 @@ 31ECDA122BED339600AE679F /* DataBrokerAuthenticationManagerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31ECDA102BED339600AE679F /* DataBrokerAuthenticationManagerBuilder.swift */; }; 31ECDA132BED339600AE679F /* DataBrokerAuthenticationManagerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31ECDA102BED339600AE679F /* DataBrokerAuthenticationManagerBuilder.swift */; }; 31ECDA142BED339600AE679F /* DataBrokerAuthenticationManagerBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31ECDA102BED339600AE679F /* DataBrokerAuthenticationManagerBuilder.swift */; }; - 31EEBADF2C85F74A001BF112 /* DuckPlayerOnboardingExperiment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31EEBADE2C85F74A001BF112 /* DuckPlayerOnboardingExperiment.swift */; }; - 31EEBAE02C85F74A001BF112 /* DuckPlayerOnboardingExperiment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31EEBADE2C85F74A001BF112 /* DuckPlayerOnboardingExperiment.swift */; }; 31EF1E802B63FFA800E6DB17 /* DBPHomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3192EC872A4DCF21001E97A5 /* DBPHomeViewController.swift */; }; 31EF1E812B63FFB800E6DB17 /* DataBrokerProtectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3139A1512AA4B3C000969C7D /* DataBrokerProtectionManager.swift */; }; 31EF1E832B63FFCA00E6DB17 /* LoginItem+DataBrokerProtection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9D8FA00B2AC5BDCE005DD0D0 /* LoginItem+DataBrokerProtection.swift */; }; @@ -3328,8 +3314,6 @@ 3199AF6C2C80734A003AEBDC /* TabModal.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabModal.swift; sourceTree = ""; }; 3199AF6D2C80734A003AEBDC /* TabModalManageable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TabModalManageable.swift; sourceTree = ""; }; 3199AF812C80736B003AEBDC /* DuckPlayerOnboardingLocationValidatorTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DuckPlayerOnboardingLocationValidatorTests.swift; sourceTree = ""; }; - 3199AF822C80736B003AEBDC /* DefaultDuckPlayerOnboardingDeciderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultDuckPlayerOnboardingDeciderTests.swift; sourceTree = ""; }; - 3199AF872C8073CA003AEBDC /* DuckPlayerOnboardingTabExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DuckPlayerOnboardingTabExtension.swift; sourceTree = ""; }; 3199C6F82AF94F5B002A7BA1 /* DataBrokerProtectionFeatureDisabler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBrokerProtectionFeatureDisabler.swift; sourceTree = ""; }; 3199C6FC2AF97367002A7BA1 /* DataBrokerProtectionAppEvents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBrokerProtectionAppEvents.swift; sourceTree = ""; }; 31A2FD162BAB41C500D0E741 /* DataBrokerProtectionFeatureGatekeeperTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBrokerProtectionFeatureGatekeeperTests.swift; sourceTree = ""; }; @@ -3337,20 +3321,15 @@ 31AA6B962B960B870025014E /* DataBrokerProtectionLoginItemPixels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBrokerProtectionLoginItemPixels.swift; sourceTree = ""; }; 31B4AF522901A4F20013585E /* NSEventExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSEventExtension.swift; sourceTree = ""; }; 31C3CE0128EDC1E70002C24A /* CustomRoundedCornersShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomRoundedCornersShape.swift; sourceTree = ""; }; - 31C3F9352C8636C2005556D1 /* DuckPlayerOnboardingExperimentPixel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckPlayerOnboardingExperimentPixel.swift; sourceTree = ""; }; - 31C3F9382C864087005556D1 /* DuckPlayerOnboardingExperiment+Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DuckPlayerOnboardingExperiment+Logger.swift"; sourceTree = ""; }; 31C5FFB82AF64D120008A79F /* DataBrokerProtectionFeatureGatekeeper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBrokerProtectionFeatureGatekeeper.swift; sourceTree = ""; }; 31C9ADE42AF0564500CEF57D /* WaitlistFeatureSetupHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitlistFeatureSetupHandler.swift; sourceTree = ""; }; 31CF3431288B0B1B0087244B /* NavigationBarBadgeAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarBadgeAnimator.swift; sourceTree = ""; }; 31D5375B291D944100407A95 /* PasswordManagementBitwardenItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordManagementBitwardenItemView.swift; sourceTree = ""; }; - 31D5CE622C89AAAF00BCB0D7 /* DuckPlayerExperimentDatePixelsUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckPlayerExperimentDatePixelsUtilities.swift; sourceTree = ""; }; - 31D5CE652C89ABC200BCB0D7 /* DuckPlayerExperimentDatePixelsUtilitiesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckPlayerExperimentDatePixelsUtilitiesTests.swift; sourceTree = ""; }; 31DC2F202BD6DE65001354EF /* DataBrokerPrerequisitesStatusVerifierTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBrokerPrerequisitesStatusVerifierTests.swift; sourceTree = ""; }; 31E163B9293A56F400963C10 /* BrokenSiteReportingReferenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrokenSiteReportingReferenceTests.swift; sourceTree = ""; }; 31E163BC293A579E00963C10 /* PrivacyReferenceTestHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyReferenceTestHelper.swift; sourceTree = ""; }; 31E163BF293A581900963C10 /* privacy-reference-tests */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = "privacy-reference-tests"; path = "Submodules/privacy-reference-tests"; sourceTree = SOURCE_ROOT; }; 31ECDA102BED339600AE679F /* DataBrokerAuthenticationManagerBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBrokerAuthenticationManagerBuilder.swift; sourceTree = ""; }; - 31EEBADE2C85F74A001BF112 /* DuckPlayerOnboardingExperiment.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DuckPlayerOnboardingExperiment.swift; sourceTree = ""; }; 31F28C4C28C8EEC500119F70 /* YoutubePlayerUserScript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YoutubePlayerUserScript.swift; sourceTree = ""; }; 31F28C4E28C8EEC500119F70 /* YoutubeOverlayUserScript.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = YoutubeOverlayUserScript.swift; sourceTree = ""; }; 31F28C5228C8EECA00119F70 /* DuckURLSchemeHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DuckURLSchemeHandler.swift; sourceTree = ""; }; @@ -5284,21 +5263,9 @@ path = Resources; sourceTree = ""; }; - 31EEBADD2C85F73E001BF112 /* Experiment */ = { - isa = PBXGroup; - children = ( - 31EEBADE2C85F74A001BF112 /* DuckPlayerOnboardingExperiment.swift */, - 31C3F9352C8636C2005556D1 /* DuckPlayerOnboardingExperimentPixel.swift */, - 31C3F9382C864087005556D1 /* DuckPlayerOnboardingExperiment+Logger.swift */, - 31D5CE622C89AAAF00BCB0D7 /* DuckPlayerExperimentDatePixelsUtilities.swift */, - ); - path = Experiment; - sourceTree = ""; - }; 31F28C4B28C8EE9000119F70 /* YoutubePlayer */ = { isa = PBXGroup; children = ( - 31EEBADD2C85F73E001BF112 /* Experiment */, 3199AF6B2C80734A003AEBDC /* Onboarding */, 3199AF6E2C80734A003AEBDC /* TabModal */, 37F19A6928E2F2D000740DC6 /* DuckPlayer.swift */, @@ -5397,11 +5364,9 @@ 376718FE28E58504003A2A15 /* YoutubePlayer */ = { isa = PBXGroup; children = ( - 3199AF822C80736B003AEBDC /* DefaultDuckPlayerOnboardingDeciderTests.swift */, 3199AF812C80736B003AEBDC /* DuckPlayerOnboardingLocationValidatorTests.swift */, 3714B1E828EDBAAB0056C57A /* DuckPlayerTests.swift */, 567DA94429E95C3F008AC5EE /* YoutubeOverlayUserScriptTests.swift */, - 31D5CE652C89ABC200BCB0D7 /* DuckPlayerExperimentDatePixelsUtilitiesTests.swift */, ); path = YoutubePlayer; sourceTree = ""; @@ -8592,7 +8557,6 @@ B6D574B12947224C008ED1B6 /* ContentBlockingTabExtension.swift */, B6DA06E32913ECEE00225DE2 /* ContextMenuManager.swift */, B6685E4129A61C460043D2EE /* DownloadsTabExtension.swift */, - 3199AF872C8073CA003AEBDC /* DuckPlayerOnboardingTabExtension.swift */, B6C416A6294A4AE500C4F2E7 /* DuckPlayerTabExtension.swift */, B6D574B329472253008ED1B6 /* FBProtectionTabExtension.swift */, B6C00ECC292F89D9009C73A6 /* FindInPageTabExtension.swift */, @@ -10600,7 +10564,6 @@ 37A6A8F22AFCC988008580A3 /* FaviconsFetcherOnboarding.swift in Sources */, 859F30652A72A9FA00C20372 /* BookmarksBarPromptPopover.swift in Sources */, 37197EA22942441900394917 /* Tab+Dialogs.swift in Sources */, - 31C3F9372C8636C2005556D1 /* DuckPlayerOnboardingExperimentPixel.swift in Sources */, 3706FAA0293F65D500E42796 /* UserAgent.swift in Sources */, 3706FAA1293F65D500E42796 /* NSAlert+DataImport.swift in Sources */, 3706FAA2293F65D500E42796 /* MainWindow.swift in Sources */, @@ -10859,7 +10822,6 @@ 3706FB4B293F65D500E42796 /* CookieNotificationAnimationModel.swift in Sources */, 3706FB4C293F65D500E42796 /* SharingMenu.swift in Sources */, 3706FB4D293F65D500E42796 /* GrammarFeaturesManager.swift in Sources */, - 31EEBAE02C85F74A001BF112 /* DuckPlayerOnboardingExperiment.swift in Sources */, 3706FB50293F65D500E42796 /* SafariFaviconsReader.swift in Sources */, 31267C692B640C4200FEF811 /* DataBrokerProtectionFeatureGatekeeper.swift in Sources */, 3706FB51293F65D500E42796 /* NSScreenExtension.swift in Sources */, @@ -11023,7 +10985,6 @@ 1DDD3EC12B84F5D5004CBF2B /* PreferencesCookiePopupProtectionView.swift in Sources */, 3706FBA4293F65D500E42796 /* ContentOverlayPopover.swift in Sources */, 3706FBA5293F65D500E42796 /* TabShadowView.swift in Sources */, - 31D5CE642C89AAAF00BCB0D7 /* DuckPlayerExperimentDatePixelsUtilities.swift in Sources */, 3706FBA7293F65D500E42796 /* EncryptedValueTransformer.swift in Sources */, 4B41EDAF2B168AFF001EEDF4 /* UnifiedFeedbackFormViewController.swift in Sources */, 31EF1E802B63FFA800E6DB17 /* DBPHomeViewController.swift in Sources */, @@ -11248,7 +11209,6 @@ F1C70D7D2BFF510000599292 /* SubscriptionEnvironment+Default.swift in Sources */, 3706FC2C293F65D500E42796 /* BookmarkHTMLReader.swift in Sources */, 3706FC2D293F65D500E42796 /* Tab+NSSecureCoding.swift in Sources */, - 31C3F93A2C864087005556D1 /* DuckPlayerOnboardingExperiment+Logger.swift in Sources */, 3706FC2E293F65D500E42796 /* NSNotificationName+EmailManager.swift in Sources */, 37445F9D2A1569F00029F789 /* SyncBookmarksAdapter.swift in Sources */, B6685E4029A606190043D2EE /* WorkspaceProtocol.swift in Sources */, @@ -11389,7 +11349,6 @@ 3706FC82293F65D500E42796 /* PermissionAuthorizationPopover.swift in Sources */, 4BCBE4552BA7E16600FC75A1 /* NetworkProtectionSubscriptionEventHandler.swift in Sources */, 371209312C233D69003ADF3D /* RemoteMessagingStoreErrorHandling.swift in Sources */, - 3199AF892C8073CB003AEBDC /* DuckPlayerOnboardingTabExtension.swift in Sources */, 3706FC83293F65D500E42796 /* PopoverMessageViewController.swift in Sources */, 7B5A23702C46A116007213AC /* ExcludedDomainsModel.swift in Sources */, 9D9AE86E2AA76D1F0026E7DC /* LoginItem+NetworkProtection.swift in Sources */, @@ -11505,7 +11464,6 @@ 9FBD84782BB3E54200220859 /* InstallationAttributionPixelHandlerTests.swift in Sources */, 3706FDFB293F661700E42796 /* DispatchQueueExtensionsTests.swift in Sources */, 9F180D102B69C553000D695F /* Tab+WKUIDelegateTests.swift in Sources */, - 31D5CE672C89ABC200BCB0D7 /* DuckPlayerExperimentDatePixelsUtilitiesTests.swift in Sources */, 981E20B6299A39B8002B68CD /* BookmarkMigrationTests.swift in Sources */, 3767319F2C7F416200EB097B /* CustomBackgroundTests.swift in Sources */, 3706FDFC293F661700E42796 /* PasswordManagementItemModelTests.swift in Sources */, @@ -11678,7 +11636,6 @@ 3706FE51293F661700E42796 /* SafariBookmarksReaderTests.swift in Sources */, 3706FE52293F661700E42796 /* FileSystemDSLTests.swift in Sources */, 3706FE53293F661700E42796 /* CoreDataEncryptionTests.swift in Sources */, - 3199AF862C80736C003AEBDC /* DefaultDuckPlayerOnboardingDeciderTests.swift in Sources */, 3706FE54293F661700E42796 /* PasteboardBookmarkTests.swift in Sources */, 3706FE55293F661700E42796 /* CBRCompileTimeReporterTests.swift in Sources */, B6E6BA172BA2CF60008AA7E1 /* SandboxTestToolNotifications.swift in Sources */, @@ -12163,7 +12120,6 @@ 1D39E5772C2BFD5700757339 /* ReleaseNotesTabExtension.swift in Sources */, 987799F12999993C005D8EB6 /* LegacyBookmarkStore.swift in Sources */, 1D220BF82B86192200F8BBC6 /* PreferencesEmailProtectionView.swift in Sources */, - 31D5CE632C89AAAF00BCB0D7 /* DuckPlayerExperimentDatePixelsUtilities.swift in Sources */, 4B8AC93526B3B2FD00879451 /* NSAlert+DataImport.swift in Sources */, AA7412BD24D2BEEE00D22FE0 /* MainWindow.swift in Sources */, AAD6D8882696DF6D002393B3 /* CrashReportPromptViewController.swift in Sources */, @@ -12238,7 +12194,6 @@ 8589063C267BCDC000D23B0D /* SaveCredentialsViewController.swift in Sources */, 4BBE0AA727B9B027003B37A8 /* PopUpButton.swift in Sources */, AABEE6A524AA0A7F0043105B /* SuggestionViewController.swift in Sources */, - 31C3F9392C864087005556D1 /* DuckPlayerOnboardingExperiment+Logger.swift in Sources */, 1D6216B229069BBF00386B2C /* BWKeyStorage.swift in Sources */, AA7E919F287872EA00AB6B62 /* VisitViewModel.swift in Sources */, 7BD7B0012C19D3830039D20A /* VPNIPCResources.swift in Sources */, @@ -12432,14 +12387,11 @@ B63ED0E526BB8FB900A9DAD1 /* SharingMenu.swift in Sources */, AA4FF40C2624751A004E2377 /* GrammarFeaturesManager.swift in Sources */, 4B9DB0442A983B24000927DB /* WaitlistModalViewController.swift in Sources */, - 31C3F9362C8636C2005556D1 /* DuckPlayerOnboardingExperimentPixel.swift in Sources */, B6DA06E8291401D700225DE2 /* WKMenuItemIdentifier.swift in Sources */, 4B0AACAE28BC6FD0001038AC /* SafariFaviconsReader.swift in Sources */, B60293E62BA19ECD0033186B /* NetPPopoverManagerMock.swift in Sources */, B6B3E0E12657EA7A0040E0A2 /* NSScreenExtension.swift in Sources */, - 31EEBADF2C85F74A001BF112 /* DuckPlayerOnboardingExperiment.swift in Sources */, B65E6BA026D9F10600095F96 /* NSBezierPathExtension.swift in Sources */, - 3199AF882C8073CB003AEBDC /* DuckPlayerOnboardingTabExtension.swift in Sources */, 31ECDA112BED339600AE679F /* DataBrokerAuthenticationManagerBuilder.swift in Sources */, 4B4D60E02A0C875F00BCD287 /* Bundle+VPN.swift in Sources */, AA6820E425502F19005ED0D5 /* WebsiteDataStore.swift in Sources */, @@ -13182,7 +13134,6 @@ 56A053FF2C1AEFA1007D8FAB /* OnboardingManagerTests.swift in Sources */, 4B4F72EC266B2ED300814C60 /* CollectionExtension.swift in Sources */, C172E7332C93759C00521D9A /* SyncPromoManagerTests.swift in Sources */, - 31D5CE662C89ABC200BCB0D7 /* DuckPlayerExperimentDatePixelsUtilitiesTests.swift in Sources */, AAE39D1B24F44885008EF28B /* TabCollectionViewModelDelegateMock.swift in Sources */, 9F0A2CF82B96A58600C5B8C0 /* BaseBookmarkEntityTests.swift in Sources */, 373A1AAA283ED86C00586521 /* BookmarksHTMLReaderTests.swift in Sources */, @@ -13322,7 +13273,6 @@ 376C4DB928A1A48A00CC0F5B /* FirePopoverViewModelTests.swift in Sources */, AAEC74B62642CC6A00C2EFBC /* HistoryStoringMock.swift in Sources */, AA652CB125DD825B009059CC /* LocalBookmarkStoreTests.swift in Sources */, - 3199AF852C80736C003AEBDC /* DefaultDuckPlayerOnboardingDeciderTests.swift in Sources */, B630794226731F5400DCEE41 /* WKDownloadMock.swift in Sources */, B6C0B24626E9CB190031CB7F /* RunLoopExtensionTests.swift in Sources */, 56D145F129E6F06D00E3488A /* MockBookmarkManager.swift in Sources */, diff --git a/DuckDuckGo/Menus/MainMenu.swift b/DuckDuckGo/Menus/MainMenu.swift index b540f08adf..2c15093f93 100644 --- a/DuckDuckGo/Menus/MainMenu.swift +++ b/DuckDuckGo/Menus/MainMenu.swift @@ -605,7 +605,6 @@ final class MainMenu: NSMenu { NSMenuItem(title: "Reset Pixels Storage", action: #selector(MainViewController.resetDailyPixels)) NSMenuItem(title: "Reset Remote Messages", action: #selector(AppDelegate.resetRemoteMessages)) NSMenuItem(title: "Reset CPM Experiment Cohort (needs restart)", action: #selector(AppDelegate.resetCpmCohort)) - NSMenuItem(title: "Reset Duck Player Onboarding", action: #selector(MainViewController.resetDuckPlayerOnboarding)) NSMenuItem(title: "Reset Duck Player Preferences", action: #selector(MainViewController.resetDuckPlayerPreferences)) NSMenuItem(title: "Reset Sync Promo prompts", action: #selector(MainViewController.resetSyncPromoPrompts)) diff --git a/DuckDuckGo/Menus/MainMenuActions.swift b/DuckDuckGo/Menus/MainMenuActions.swift index 4b8efc9ad1..6ab3b79972 100644 --- a/DuckDuckGo/Menus/MainMenuActions.swift +++ b/DuckDuckGo/Menus/MainMenuActions.swift @@ -834,11 +834,6 @@ extension MainViewController { UserDefaults.standard.set(true, forKey: UserDefaultsWrapper.Key.homePageShowEmailProtection.rawValue) } - @objc func resetDuckPlayerOnboarding(_ sender: Any?) { - DefaultDuckPlayerOnboardingDecider().reset() - DuckPlayerOnboardingExperiment().reset() - } - @objc func resetDuckPlayerPreferences(_ sender: Any?) { DuckPlayerPreferences.shared.reset() } diff --git a/DuckDuckGo/Statistics/GeneralPixel.swift b/DuckDuckGo/Statistics/GeneralPixel.swift index 5046114dca..dc2a526ced 100644 --- a/DuckDuckGo/Statistics/GeneralPixel.swift +++ b/DuckDuckGo/Statistics/GeneralPixel.swift @@ -1172,6 +1172,7 @@ enum GeneralPixel: PixelKitEventV2 { case .onboardingDuckplayerUsed5to7(let cohort): return [PixelKit.Parameters.experimentCohort: cohort] + /// Duck Player pixels case .duckPlayerDailyUniqueView, .duckPlayerViewFromYoutubeViaMainOverlay, .duckPlayerViewFromYoutubeViaHoverButton, @@ -1196,7 +1197,7 @@ enum GeneralPixel: PixelKitEventV2 { .duckPlayerContingencySettingsDisplayed, .duckPlayerWeeklyUniqueView, .duckPlayerContingencyLearnMoreClicked: - return DuckPlayerOnboardingExperiment().getPixelParameters() + return nil case .bookmarksSortButtonClicked(let origin), .bookmarksSortButtonDismissed(let origin), diff --git a/DuckDuckGo/Tab/Model/Tab+Navigation.swift b/DuckDuckGo/Tab/Model/Tab+Navigation.swift index 1a4bce2d59..ab3fc72e87 100644 --- a/DuckDuckGo/Tab/Model/Tab+Navigation.swift +++ b/DuckDuckGo/Tab/Model/Tab+Navigation.swift @@ -54,9 +54,6 @@ extension Tab: NavigationResponder { // Duck Player overlay navigations handling .weak(nullable: self.duckPlayer), - // Duck Player onboarding banner - .weak(nullable: self.duckPlayerOnboarding), - // open external scheme link in another app .weak(nullable: self.externalAppSchemeHandler), diff --git a/DuckDuckGo/Tab/TabExtensions/DuckPlayerOnboardingTabExtension.swift b/DuckDuckGo/Tab/TabExtensions/DuckPlayerOnboardingTabExtension.swift deleted file mode 100644 index 7ff8b9769f..0000000000 --- a/DuckDuckGo/Tab/TabExtensions/DuckPlayerOnboardingTabExtension.swift +++ /dev/null @@ -1,82 +0,0 @@ -// -// DuckPlayerOnboardingTabExtension.swift -// -// Copyright © 2024 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import Navigation -import Combine - -typealias DuckPlayerOnboardingPublisher = AnyPublisher - -final class DuckPlayerOnboardingTabExtension: TabExtension { - @Published private(set) var onboardingState: OnboardingState? - private let onboardingDecider: DuckPlayerOnboardingDecider - private let experimentManager: OnboardingExperimentManager - - init(onboardingDecider: DuckPlayerOnboardingDecider, - experimentManager: OnboardingExperimentManager = DuckPlayerOnboardingExperiment()) { - self.onboardingDecider = onboardingDecider - self.experimentManager = experimentManager - } -} - -extension DuckPlayerOnboardingTabExtension: NavigationResponder { - - func navigationDidFinish(_ navigation: Navigation) { - let locationValidator = DuckPlayerOnboardingLocationValidator() - - Task { @MainActor in - if let webView = navigation.navigationAction.targetFrame?.webView, - await locationValidator.isValidLocation(webView) { - - self.experimentManager.assignUserToCohort() - - if onboardingDecider.canDisplayOnboarding { - onboardingState = .init(onboardingDecider: onboardingDecider) - } - } - } - } -} - -struct OnboardingState { - let onboardingDecider: DuckPlayerOnboardingDecider -} - -protocol DuckPlayerOnboardingProtocol: AnyObject, NavigationResponder { - var duckPlayerOnboardingPublisher: DuckPlayerOnboardingPublisher { get } -} - -extension DuckPlayerOnboardingTabExtension: DuckPlayerOnboardingProtocol { - func getPublicProtocol() -> DuckPlayerOnboardingProtocol { self } - - var duckPlayerOnboardingPublisher: DuckPlayerOnboardingPublisher { - self.$onboardingState.eraseToAnyPublisher() - } -} - -extension TabExtensions { - var duckPlayerOnboarding: DuckPlayerOnboardingProtocol? { - resolve(DuckPlayerOnboardingTabExtension.self) - } -} - -extension Tab { - var duckPlayerOnboardingPublisher: DuckPlayerOnboardingPublisher { - self.duckPlayerOnboarding?.duckPlayerOnboardingPublisher ?? Just(nil).eraseToAnyPublisher() - } -} diff --git a/DuckDuckGo/Tab/TabExtensions/DuckPlayerTabExtension.swift b/DuckDuckGo/Tab/TabExtensions/DuckPlayerTabExtension.swift index 5371bddcbe..316f5dde06 100644 --- a/DuckDuckGo/Tab/TabExtensions/DuckPlayerTabExtension.swift +++ b/DuckDuckGo/Tab/TabExtensions/DuckPlayerTabExtension.swift @@ -55,20 +55,17 @@ final class DuckPlayerTabExtension { private weak var youtubePlayerScript: YoutubePlayerUserScript? private let onboardingDecider: DuckPlayerOnboardingDecider private var shouldSelectNextNewTab: Bool? - private let experimentManager: OnboardingExperimentManager init(duckPlayer: DuckPlayer, isBurner: Bool, scriptsPublisher: some Publisher, webViewPublisher: some Publisher, preferences: DuckPlayerPreferences = .shared, - onboardingDecider: DuckPlayerOnboardingDecider, - experimentManager: OnboardingExperimentManager = DuckPlayerOnboardingExperiment()) { + onboardingDecider: DuckPlayerOnboardingDecider) { self.duckPlayer = duckPlayer self.isBurner = isBurner self.preferences = preferences self.onboardingDecider = onboardingDecider - self.experimentManager = experimentManager webViewPublisher.sink { [weak self] webView in self?.webView = webView }.store(in: &cancellables) @@ -378,7 +375,6 @@ extension DuckPlayerTabExtension: NavigationResponder { frequency: .legacyDaily, withAdditionalParameters: params) - experimentManager.fireWeeklyUniqueViewPixel(extraParams: params) } } diff --git a/DuckDuckGo/Tab/TabExtensions/TabExtensions.swift b/DuckDuckGo/Tab/TabExtensions/TabExtensions.swift index 194b14a980..c72c44f584 100644 --- a/DuckDuckGo/Tab/TabExtensions/TabExtensions.swift +++ b/DuckDuckGo/Tab/TabExtensions/TabExtensions.swift @@ -201,10 +201,6 @@ extension TabExtensionsBuilder { onboardingDecider: duckPlayerOnboardingDecider) } - add { - DuckPlayerOnboardingTabExtension(onboardingDecider: duckPlayerOnboardingDecider) - } - add { SpecialErrorPageTabExtension(webViewPublisher: args.webViewFuture, scriptsPublisher: userScripts.compactMap { $0 }, diff --git a/DuckDuckGo/Tab/View/BrowserTabViewController.swift b/DuckDuckGo/Tab/View/BrowserTabViewController.swift index 2d5cb2afd7..0cbe2231a0 100644 --- a/DuckDuckGo/Tab/View/BrowserTabViewController.swift +++ b/DuckDuckGo/Tab/View/BrowserTabViewController.swift @@ -293,7 +293,6 @@ final class BrowserTabViewController: NSViewController { self.subscribeToTabContent(of: selectedTabViewModel) self.subscribeToHoveredLink(of: selectedTabViewModel) self.subscribeToUserDialogs(of: selectedTabViewModel) - self.subscribeToDuckPlayerOnboardingPrompt(of: selectedTabViewModel) self.adjustFirstResponder(force: true) self.presentContextualOnboarding() @@ -534,18 +533,6 @@ final class BrowserTabViewController: NSViewController { #endif } - private func subscribeToDuckPlayerOnboardingPrompt(of tabViewModel: TabViewModel?) { - tabViewModel?.tab.duckPlayerOnboardingPublisher.sink { [weak self, weak tab = tabViewModel?.tab] onboardingState in - - guard let self, tab != nil, let onboardingState = onboardingState, onboardingState.onboardingDecider.canDisplayOnboarding else { - self?.duckPlayerOnboardingModalManager.close(animated: false, completion: nil) - return - } - - self.duckPlayerOnboardingModalManager.show(on: self.view, animated: true) - }.store(in: &tabViewModelCancellables) - } - private func shouldMakeContentViewFirstResponder(for tabContent: Tab.TabContent) -> Bool { // always steal focus when first responder is not a text field guard view.window?.firstResponder is NSText else { diff --git a/DuckDuckGo/YoutubePlayer/Experiment/DuckPlayerExperimentDatePixelsUtilities.swift b/DuckDuckGo/YoutubePlayer/Experiment/DuckPlayerExperimentDatePixelsUtilities.swift deleted file mode 100644 index 0168370595..0000000000 --- a/DuckDuckGo/YoutubePlayer/Experiment/DuckPlayerExperimentDatePixelsUtilities.swift +++ /dev/null @@ -1,54 +0,0 @@ -// -// DuckPlayerExperimentDatePixelsUtilities.swift -// -// Copyright © 2024 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation - -enum Frequency: Int { - case daily = 1 - case weekly = 7 -} - -struct DuckPlayerExperimentDatePixelsUtilities { - private static let calendar = Calendar.current - - /// Determines whether a pixel should fire based on the start and end dates and the specified frequency. - /// - /// - Parameters: - /// - startDate: The date from which to start the calculation. - /// - endDate: The date at which to end the calculation. - /// - daysDifference: The frequency that determines the minimum number of days required to fire the pixel. - /// - Returns: A Boolean value indicating whether the pixel should fire. - static func shouldFirePixel(startDate: Date, endDate: Date, daysDifference: Frequency) -> Bool { - if let differenceBetweenDates = numberOfDaysFrom(startDate: startDate, endDate: endDate) { - return differenceBetweenDates >= daysDifference.rawValue - } - - return false - } - - /// Calculates the number of days between two dates. - /// - /// - Parameters: - /// - startDate: The starting date for the calculation. - /// - endDate: The ending date for the calculation. - /// - Returns: The number of days between the two dates, or `nil` if the calculation fails. - static func numberOfDaysFrom(startDate: Date, endDate: Date) -> Int? { - let components = calendar.dateComponents([.day], from: startDate, to: endDate) - return components.day - } -} diff --git a/DuckDuckGo/YoutubePlayer/Experiment/DuckPlayerOnboardingExperiment+Logger.swift b/DuckDuckGo/YoutubePlayer/Experiment/DuckPlayerOnboardingExperiment+Logger.swift deleted file mode 100644 index 65ce21e87b..0000000000 --- a/DuckDuckGo/YoutubePlayer/Experiment/DuckPlayerOnboardingExperiment+Logger.swift +++ /dev/null @@ -1,24 +0,0 @@ -// -// DuckPlayerOnboardingExperiment+Logger.swift -// -// Copyright © 2024 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import os.log - -public extension Logger { - static var duckPlayerOnboardingExperiment: Logger = { Logger(subsystem: "DuckPlayer Onboarding Experiment", category: "DuckPlayer") }() -} diff --git a/DuckDuckGo/YoutubePlayer/Experiment/DuckPlayerOnboardingExperiment.swift b/DuckDuckGo/YoutubePlayer/Experiment/DuckPlayerOnboardingExperiment.swift deleted file mode 100644 index 3bb9e81541..0000000000 --- a/DuckDuckGo/YoutubePlayer/Experiment/DuckPlayerOnboardingExperiment.swift +++ /dev/null @@ -1,175 +0,0 @@ -// -// DuckPlayerOnboardingExperiment.swift -// -// Copyright © 2024 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import PixelKit -import os.log - -protocol OnboardingExperimentManager { - /// Assigns the user to a cohort for the onboarding experiment. - func assignUserToCohort() - - /// Retrieves pixel parameters for tracking the experiment. - /// - Parameters: - /// - cohort: A Boolean indicating whether to include the cohort information. - /// - date: A Boolean indicating whether to include the enrollment date. - /// - experimentName: A Boolean indicating whether to include the experiment name. - /// - Returns: A dictionary containing pixel parameters, or nil if no parameters are available. - func getPixelParameters(cohort: Bool, date: Bool, experimentName: Bool) -> [String: String]? - - /// Fires a pixel for tracking unique views on a weekly basis. - /// - Parameter extraParams: Additional parameters to include with the pixel event. - func fireWeeklyUniqueViewPixel(extraParams: [String: String]?) - - /// A Boolean value indicating whether the user is assigned to the experiment cohort. - var isUserAssignedToExperimentCohort: Bool { get } -} - -// https://app.asana.com/0/72649045549333/1208088257884523/f -struct DuckPlayerOnboardingExperiment: OnboardingExperimentManager { - private let userDefaults: UserDefaults - - init(userDefault: UserDefaults = .standard) { - self.userDefaults = userDefault - } - - enum Cohort: String { - case control - case experiment - } - - private enum ExperimentPixelValues { - static let name = "priming-modal" - } - - private enum ExperimentPixelKeys { - static let variant = "variant" - static let enrollment = "enrollment" - static let experimentName = "expname" - static let onboardingCohort = "onboarding-cohort" - } - - var isUserAssignedToExperimentCohort: Bool { - guard enrollmentDate != nil else { return false } - return experimentCohort == .experiment - } - - func assignUserToCohort() { - guard !userDefaults.didRunEnrollment else { - Logger.duckPlayerOnboardingExperiment.debug("Cohort already assigned, skipping...") - return - } - - let cohort: Cohort = Bool.random() ? .experiment : .control - userDefaults.experimentCohort = cohort - userDefaults.enrollmentDate = Date() - userDefaults.didRunEnrollment = true - - PixelKit.fire(NonStandardEvent(DuckPlayerOnboardingExperimentPixel.enrollmentPixel)) - Logger.duckPlayerOnboardingExperiment.debug("User assigned to cohort \(cohort.rawValue)") - } - - func fireWeeklyUniqueViewPixel(extraParams: [String: String]?) { - guard enrollmentDate != nil else { return } - - if shouldFireWeeklyPixel() { - PixelKit.fire(NonStandardEvent(DuckPlayerOnboardingExperimentPixel.weeklyUniqueView), - withAdditionalParameters: extraParams) - userDefaults.weeklyPixelSentDate = Date() - } - } - - func getPixelParameters(cohort: Bool = true, - date: Bool = true, - experimentName: Bool = true) -> [String: String]? { - var parameters: [String: String] = [:] - - if let experimentCohort = experimentCohort, cohort { - parameters[ExperimentPixelKeys.variant] = experimentCohort.rawValue - } - - if let enrollmentDate = enrollmentDate, date { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yyyyMMdd" - let enrollmentDateString = dateFormatter.string(from: enrollmentDate) - parameters[ExperimentPixelKeys.enrollment] = enrollmentDateString - } - - if experimentName { - parameters[ExperimentPixelKeys.experimentName] = ExperimentPixelValues.name - } - - if PixelExperiment.isExperimentInstalled, - let onboardingExperimentCohort = PixelExperiment.logic.allocatedCohort { - parameters[ExperimentPixelKeys.onboardingCohort] = onboardingExperimentCohort - } - - return parameters.isEmpty ? nil : parameters - } - - private var enrollmentDate: Date? { - return userDefaults.enrollmentDate - } - - private var experimentCohort: Cohort? { - return userDefaults.experimentCohort - } - - func reset() { - userDefaults.enrollmentDate = nil - userDefaults.experimentCohort = nil - userDefaults.didRunEnrollment = false - userDefaults.weeklyPixelSentDate = nil - } - - private func shouldFireWeeklyPixel() -> Bool { - guard let lastFiredDate = userDefaults.weeklyPixelSentDate else { return true } - return DuckPlayerExperimentDatePixelsUtilities.shouldFirePixel(startDate: lastFiredDate, - endDate: Date(), - daysDifference: .weekly) - } -} - -private extension UserDefaults { - enum Keys { - static let enrollmentDate = "duckplayer.onboarding.experiment-enrollment-date" - static let experimentCohort = "duckplayer.onboarding.experiment-cohort" - static let didRunEnrollment = "duckplayer.onboarding.experiment-did-run-enrollment" - static let weeklyPixelSentDate = "duckplayer.onboarding.experiment-weekly-pixel-sent-date" - } - - var enrollmentDate: Date? { - get { return object(forKey: Keys.enrollmentDate) as? Date } - set { set(newValue, forKey: Keys.enrollmentDate) } - } - - var weeklyPixelSentDate: Date? { - get { return object(forKey: Keys.weeklyPixelSentDate) as? Date } - set { set(newValue, forKey: Keys.weeklyPixelSentDate) } - } - - var experimentCohort: DuckPlayerOnboardingExperiment.Cohort? { - get { return DuckPlayerOnboardingExperiment.Cohort(rawValue: string(forKey: Keys.experimentCohort) ?? "") } - set { set(newValue?.rawValue, forKey: Keys.experimentCohort) } - } - - var didRunEnrollment: Bool { - get { return bool(forKey: Keys.didRunEnrollment) } - set { set(newValue, forKey: Keys.didRunEnrollment) } - } -} diff --git a/DuckDuckGo/YoutubePlayer/Experiment/DuckPlayerOnboardingExperimentPixel.swift b/DuckDuckGo/YoutubePlayer/Experiment/DuckPlayerOnboardingExperimentPixel.swift deleted file mode 100644 index 52e68d5fe8..0000000000 --- a/DuckDuckGo/YoutubePlayer/Experiment/DuckPlayerOnboardingExperimentPixel.swift +++ /dev/null @@ -1,57 +0,0 @@ -// -// DuckPlayerOnboardingExperimentPixel.swift -// -// Copyright © 2024 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import PixelKit - -// https://app.asana.com/0/72649045549333/1208088257884523/f -enum DuckPlayerOnboardingExperimentPixel: PixelKitEventV2 { - - case enrollmentPixel - case weeklyUniqueView - case modalAccept - case modalReject - - var name: String { - switch self { - case .enrollmentPixel: - "duckplayer_experiment_cohort_assign" - case .weeklyUniqueView: - "duckplayer_weekly-unique-view" - case .modalAccept: - "duckplayer_experiment_modal-accept" - case .modalReject: - "duckplayer_experiment_modal-reject" - } - } - - var parameters: [String: String]? { - switch self { - case .enrollmentPixel: - return DuckPlayerOnboardingExperiment().getPixelParameters(date: false) - case .weeklyUniqueView, - .modalAccept, - .modalReject: - return DuckPlayerOnboardingExperiment().getPixelParameters() - } - } - - var error: (any Error)? { - nil - } -} diff --git a/DuckDuckGo/YoutubePlayer/Onboarding/DuckPlayerOnboardingModal/DuckPlayerOnboardingDecider.swift b/DuckDuckGo/YoutubePlayer/Onboarding/DuckPlayerOnboardingModal/DuckPlayerOnboardingDecider.swift index f9ba84090f..6c9a32ce29 100644 --- a/DuckDuckGo/YoutubePlayer/Onboarding/DuckPlayerOnboardingModal/DuckPlayerOnboardingDecider.swift +++ b/DuckDuckGo/YoutubePlayer/Onboarding/DuckPlayerOnboardingModal/DuckPlayerOnboardingDecider.swift @@ -60,15 +60,12 @@ struct DefaultDuckPlayerOnboardingDecider: DuckPlayerOnboardingDecider { private let defaults: UserDefaults private var observer: NSObjectProtocol? private let preferences: DuckPlayerPreferences - private let onboardingExperiment: OnboardingExperimentManager var valueChangedPublisher: PassthroughSubject = .init() - init(defaults: UserDefaults = .standard, preferences: DuckPlayerPreferences = .shared, - onboardingExperiment: OnboardingExperimentManager = DuckPlayerOnboardingExperiment()) { + init(defaults: UserDefaults = .standard, preferences: DuckPlayerPreferences = .shared) { self.defaults = defaults self.preferences = preferences - self.onboardingExperiment = onboardingExperiment observer = NotificationCenter.default.addObserver(forName: .valuesDidChange, object: nil, queue: nil) { [valuesDidChange] _ in valuesDidChange() } @@ -76,7 +73,7 @@ struct DefaultDuckPlayerOnboardingDecider: DuckPlayerOnboardingDecider { /// We only want to display the onboarding if it was never displayed, the settings is set to alwaysAsk and haven't interacted with the overlay. var canDisplayOnboarding: Bool { - return onboardingExperiment.isUserAssignedToExperimentCohort && !defaults.onboardingWasDisplayed && preferences.duckPlayerMode == .alwaysAsk + return !defaults.onboardingWasDisplayed && preferences.duckPlayerMode == .alwaysAsk } var shouldOpenFirstVideoOnDuckPlayer: Bool { diff --git a/DuckDuckGo/YoutubePlayer/Onboarding/DuckPlayerOnboardingModal/DuckPlayerOnboardingViewModel.swift b/DuckDuckGo/YoutubePlayer/Onboarding/DuckPlayerOnboardingModal/DuckPlayerOnboardingViewModel.swift index 742ec66789..95f7476ef4 100644 --- a/DuckDuckGo/YoutubePlayer/Onboarding/DuckPlayerOnboardingModal/DuckPlayerOnboardingViewModel.swift +++ b/DuckDuckGo/YoutubePlayer/Onboarding/DuckPlayerOnboardingModal/DuckPlayerOnboardingViewModel.swift @@ -45,13 +45,11 @@ final class DuckPlayerOnboardingViewModel: ObservableObject { onboardingDecider.setOpenFirstVideoOnDuckPlayer() onboardingDecider.setOnboardingAsDone() - PixelKit.fire(NonStandardEvent(DuckPlayerOnboardingExperimentPixel.modalAccept)) } func handleNotNowCTA() { delegate?.duckPlayerOnboardingViewModelDidSelectNotNow(self) onboardingDecider.setOnboardingAsDone() - PixelKit.fire(NonStandardEvent(DuckPlayerOnboardingExperimentPixel.modalReject)) } func handleGotItCTA() { diff --git a/UnitTests/YoutubePlayer/DefaultDuckPlayerOnboardingDeciderTests.swift b/UnitTests/YoutubePlayer/DefaultDuckPlayerOnboardingDeciderTests.swift deleted file mode 100644 index b68bac0d95..0000000000 --- a/UnitTests/YoutubePlayer/DefaultDuckPlayerOnboardingDeciderTests.swift +++ /dev/null @@ -1,177 +0,0 @@ -// -// DefaultDuckPlayerOnboardingDeciderTests.swift -// -// Copyright © 2024 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import XCTest -@testable import DuckDuckGo_Privacy_Browser - -final class DefaultDuckPlayerOnboardingDeciderTests: XCTestCase { - - var decider: DefaultDuckPlayerOnboardingDecider! - var defaults: UserDefaults! - static let defaultsName = "TestDefaults" - var experiment: MockOnboardingExperimentManager! - - override func setUp() { - super.setUp() - defaults = UserDefaults(suiteName: DefaultDuckPlayerOnboardingDeciderTests.defaultsName)! - experiment = MockOnboardingExperimentManager() - experiment.cohortUser = true - decider = DefaultDuckPlayerOnboardingDecider(defaults: defaults, onboardingExperiment: experiment) - } - - override func tearDown() { - super.tearDown() - defaults.removePersistentDomain(forName: DefaultDuckPlayerOnboardingDeciderTests.defaultsName) - experiment.reset() - } - - func testCanDisplayOnboarding_InitiallyReturnsTrue() { - XCTAssertTrue(decider.canDisplayOnboarding) - } - - func testCanDisplayOnboardingNotOnCohort_InitiallyReturnsTrue() { - experiment.cohortUser = false - XCTAssertFalse(decider.canDisplayOnboarding) - } - - func testCanDisplayOnboarding_ReturnsFalseAfterSettingOnboardingAsDone() { - decider.setOnboardingAsDone() - XCTAssertFalse(decider.canDisplayOnboarding) - } - - func testShouldOpenFirstVideoOnDuckPlayer_InitiallyReturnsFalse() { - XCTAssertFalse(decider.shouldOpenFirstVideoOnDuckPlayer) - } - - func testShouldOpenFirstVideoOnDuckPlayer_ReturnsTrueAfterSettingOpenFirstVideo() { - decider.setOpenFirstVideoOnDuckPlayer() - XCTAssertTrue(decider.shouldOpenFirstVideoOnDuckPlayer) - } - - func testShouldOpenFirstVideoOnDuckPlayer_ReturnsFalseAfterSettingFirstVideoAsDone() { - decider.setOpenFirstVideoOnDuckPlayer() - XCTAssertTrue(decider.shouldOpenFirstVideoOnDuckPlayer) - - decider.setFirstVideoInDuckPlayerAsDone() - XCTAssertFalse(decider.shouldOpenFirstVideoOnDuckPlayer) - } - - func testSetOnboardingAsDone_canDisplayOnboardingReturnsFalse() { - XCTAssertTrue(decider.canDisplayOnboarding) - decider.setOnboardingAsDone() - XCTAssertFalse(decider.canDisplayOnboarding) - } - - func testCanDisplayOnboarding_WhenAlwaysAskAndNotInteracted() { - let preferences = DuckPlayerPreferences( - persistor: DuckPlayerPreferencesPersistorMock( - duckPlayerMode: .alwaysAsk, - youtubeOverlayInteracted: false, - youtubeOverlayAnyButtonPressed: false - ) - ) - - let onboardingDecider = DefaultDuckPlayerOnboardingDecider(defaults: defaults, - preferences: preferences, - onboardingExperiment: experiment) - XCTAssertTrue(onboardingDecider.canDisplayOnboarding) - } - - func testCanDisplayOnboarding_WhenAlwaysAskAndInteracted() { - let preferences = DuckPlayerPreferences( - persistor: DuckPlayerPreferencesPersistorMock( - duckPlayerMode: .alwaysAsk, - youtubeOverlayInteracted: false, - youtubeOverlayAnyButtonPressed: true - ) - ) - - let onboardingDecider = DefaultDuckPlayerOnboardingDecider(defaults: defaults, - preferences: preferences, - onboardingExperiment: experiment) - XCTAssertTrue(onboardingDecider.canDisplayOnboarding) - } - - func testCanDisplayOnboarding_WhenEnabled() { - let preferences = DuckPlayerPreferences( - persistor: DuckPlayerPreferencesPersistorMock( - duckPlayerMode: .enabled, - youtubeOverlayInteracted: false, - youtubeOverlayAnyButtonPressed: false - ) - ) - - let onboardingDecider = DefaultDuckPlayerOnboardingDecider(defaults: defaults, preferences: preferences) - XCTAssertFalse(onboardingDecider.canDisplayOnboarding) - } - - func testCanDisplayOnboarding_WhenDisabled() { - let preferences = DuckPlayerPreferences( - persistor: DuckPlayerPreferencesPersistorMock( - duckPlayerMode: .disabled, - youtubeOverlayInteracted: false, - youtubeOverlayAnyButtonPressed: false - ) - ) - - let onboardingDecider = DefaultDuckPlayerOnboardingDecider(defaults: defaults, preferences: preferences) - XCTAssertFalse(onboardingDecider.canDisplayOnboarding) - } - - func testCanDisplayOnboarding_WhenOnboardingWasDisplayed() { - let preferences = DuckPlayerPreferences( - persistor: DuckPlayerPreferencesPersistorMock( - duckPlayerMode: .alwaysAsk, - youtubeOverlayInteracted: false, - youtubeOverlayAnyButtonPressed: false - ) - ) - - let onboardingDecider = DefaultDuckPlayerOnboardingDecider(defaults: defaults, preferences: preferences) - onboardingDecider.setOnboardingAsDone() - XCTAssertFalse(onboardingDecider.canDisplayOnboarding) - } - - func testReset_ResetsAllFlagsToFalse() { - decider.setOnboardingAsDone() - decider.setOpenFirstVideoOnDuckPlayer() - decider.setFirstVideoInDuckPlayerAsDone() - decider.reset() - - XCTAssertTrue(decider.canDisplayOnboarding) - XCTAssertFalse(decider.shouldOpenFirstVideoOnDuckPlayer) - } -} - -final class MockOnboardingExperimentManager: OnboardingExperimentManager { - var cohortUser: Bool = true - - func assignUserToCohort() { } - func getPixelParameters(cohort: Bool, date: Bool, experimentName: Bool) -> [String: String]? { - return nil - } - func fireWeeklyUniqueViewPixel(extraParams: [String: String]?) { } - - var isUserAssignedToExperimentCohort: Bool { - cohortUser - } - - func reset() { - cohortUser = true - } -} diff --git a/UnitTests/YoutubePlayer/DuckPlayerExperimentDatePixelsUtilitiesTests.swift b/UnitTests/YoutubePlayer/DuckPlayerExperimentDatePixelsUtilitiesTests.swift deleted file mode 100644 index 495e70a834..0000000000 --- a/UnitTests/YoutubePlayer/DuckPlayerExperimentDatePixelsUtilitiesTests.swift +++ /dev/null @@ -1,115 +0,0 @@ -// -// DuckPlayerExperimentDatePixelsUtilitiesTests.swift -// -// Copyright © 2024 DuckDuckGo. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -import Foundation -import XCTest -@testable import DuckDuckGo_Privacy_Browser - -class DuckPlayerExperimentDatePixelsUtilitiesTests: XCTestCase { - - func testShouldFirePixelDaily() { - let startDate = Calendar.current.date(from: DateComponents(year: 2024, month: 10, day: 1))! - let endDate = Calendar.current.date(from: DateComponents(year: 2024, month: 10, day: 2))! - let result = DuckPlayerExperimentDatePixelsUtilities.shouldFirePixel(startDate: startDate, endDate: endDate, daysDifference: .daily) - XCTAssertTrue(result, "Pixel should fire for daily frequency when the difference is 1 day.") - } - - func testShouldFirePixelWeeklySameMonth() { - let startDate = Calendar.current.date(from: DateComponents(year: 2024, month: 9, day: 2))! - let endDate = Calendar.current.date(from: DateComponents(year: 2024, month: 9, day: 9))! - let result = DuckPlayerExperimentDatePixelsUtilities.shouldFirePixel(startDate: startDate, endDate: endDate, daysDifference: .weekly) - XCTAssertTrue(result, "Pixel should fire for weekly frequency when the difference is 7 days.") - } - - func testShouldFirePixelWeeklyChangingMonths() { - let startDate = Calendar.current.date(from: DateComponents(year: 2024, month: 9, day: 25))! - let endDate = Calendar.current.date(from: DateComponents(year: 2024, month: 10, day: 2))! - let result = DuckPlayerExperimentDatePixelsUtilities.shouldFirePixel(startDate: startDate, endDate: endDate, daysDifference: .weekly) - XCTAssertTrue(result, "Pixel should fire for weekly frequency when the difference is 7 days.") - } - - func testShouldFirePixelWeeklyChangingYears() { - let startDate = Calendar.current.date(from: DateComponents(year: 2023, month: 12, day: 30))! - let endDate = Calendar.current.date(from: DateComponents(year: 2024, month: 1, day: 6))! - let result = DuckPlayerExperimentDatePixelsUtilities.shouldFirePixel(startDate: startDate, endDate: endDate, daysDifference: .weekly) - XCTAssertTrue(result, "Pixel should fire for weekly frequency when the difference is 7 days across years.") - } - - func testShouldNotFirePixelDaily() { - let startDate = Calendar.current.date(from: DateComponents(year: 2024, month: 10, day: 1))! - let endDate = Calendar.current.date(from: DateComponents(year: 2024, month: 10, day: 1, hour: 12))! - let result = DuckPlayerExperimentDatePixelsUtilities.shouldFirePixel(startDate: startDate, endDate: endDate, daysDifference: .daily) - XCTAssertFalse(result, "Pixel should not fire for daily frequency when the difference is less than 1 day.") - } - - func testShouldNotFirePixelWeeklySameMonth() { - let startDate = Calendar.current.date(from: DateComponents(year: 2024, month: 9, day: 2))! - let endDate = Calendar.current.date(from: DateComponents(year: 2024, month: 9, day: 8))! - let result = DuckPlayerExperimentDatePixelsUtilities.shouldFirePixel(startDate: startDate, endDate: endDate, daysDifference: .weekly) - XCTAssertFalse(result, "Pixel should not fire for weekly frequency when the difference is 6 days.") - } - - func testShouldNotFirePixelWeeklyChangingMonths() { - let startDate = Calendar.current.date(from: DateComponents(year: 2024, month: 9, day: 25))! - let endDate = Calendar.current.date(from: DateComponents(year: 2024, month: 10, day: 1))! - let result = DuckPlayerExperimentDatePixelsUtilities.shouldFirePixel(startDate: startDate, endDate: endDate, daysDifference: .weekly) - XCTAssertFalse(result, "Pixel should not fire for weekly frequency when the difference is 6 days.") - } - - func testShouldNotFirePixelWeeklyChangingYears() { - let startDate = Calendar.current.date(from: DateComponents(year: 2023, month: 12, day: 30))! - let endDate = Calendar.current.date(from: DateComponents(year: 2024, month: 1, day: 5))! - let result = DuckPlayerExperimentDatePixelsUtilities.shouldFirePixel(startDate: startDate, endDate: endDate, daysDifference: .weekly) - XCTAssertFalse(result, "Pixel should not fire for weekly frequency when the difference is 6 days across years.") - } - - func testShouldNotFirePixelWeekly() { - let startDate = Calendar.current.date(from: DateComponents(year: 2024, month: 9, day: 26))! - let endDate = Calendar.current.date(from: DateComponents(year: 2024, month: 10, day: 2))! - let result = DuckPlayerExperimentDatePixelsUtilities.shouldFirePixel(startDate: startDate, endDate: endDate, daysDifference: .weekly) - XCTAssertFalse(result, "Pixel should not fire for weekly frequency when the difference is less than 7 days.") - } - - func testNumberOfDaysFrom() { - let startDate = Calendar.current.date(from: DateComponents(year: 2024, month: 9, day: 22))! - let endDate = Calendar.current.date(from: DateComponents(year: 2024, month: 10, day: 2))! - let daysDifference = DuckPlayerExperimentDatePixelsUtilities.numberOfDaysFrom(startDate: startDate, endDate: endDate) - XCTAssertEqual(daysDifference, 10, "The number of days between the two dates should be 10.") - } - - func testNumberOfDaysFromChangingYears() { - let startDate = Calendar.current.date(from: DateComponents(year: 2023, month: 12, day: 31))! - let endDate = Calendar.current.date(from: DateComponents(year: 2024, month: 1, day: 1))! - let daysDifference = DuckPlayerExperimentDatePixelsUtilities.numberOfDaysFrom(startDate: startDate, endDate: endDate) - XCTAssertEqual(daysDifference, 1, "The number of days between December 31, 2023, and January 1, 2024, should be 1.") - } - - func testNumberOfDaysFromSameDay() { - let startDate = Calendar.current.date(from: DateComponents(year: 2024, month: 10, day: 2))! - let endDate = Calendar.current.date(from: DateComponents(year: 2024, month: 10, day: 2))! - let daysDifference = DuckPlayerExperimentDatePixelsUtilities.numberOfDaysFrom(startDate: startDate, endDate: endDate) - XCTAssertEqual(daysDifference, 0, "The number of days between the same day should be 0.") - } - - func testNumberOfDaysFromInvalidDates() { - let startDate = Calendar.current.date(from: DateComponents(year: 2024, month: 10, day: 3))! - let endDate = Calendar.current.date(from: DateComponents(year: 2024, month: 10, day: 2))! - let daysDifference = DuckPlayerExperimentDatePixelsUtilities.numberOfDaysFrom(startDate: startDate, endDate: endDate) - XCTAssertEqual(daysDifference, -1, "The number of days should be negative when start date is after end date.") - } -}