diff --git a/DuckDuckGo/NewTabPageIntroMessageSetup.swift b/DuckDuckGo/NewTabPageIntroMessageSetup.swift index 87e60c96a6..f954e41182 100644 --- a/DuckDuckGo/NewTabPageIntroMessageSetup.swift +++ b/DuckDuckGo/NewTabPageIntroMessageSetup.swift @@ -23,14 +23,21 @@ import Core struct NewTabPageIntroMessageSetup { let appSettings: AppSettings let statistics: StatisticsStore + let newTabPageManager: NewTabPageManaging - init(appSettings: AppSettings = AppDependencyProvider.shared.appSettings, statistics: StatisticsStore = StatisticsUserDefaults()) { + init(appSettings: AppSettings = AppDependencyProvider.shared.appSettings, + statistics: StatisticsStore = StatisticsUserDefaults(), + newTabPageManager: NewTabPageManaging = NewTabPageManager()) { self.appSettings = appSettings self.statistics = statistics + self.newTabPageManager = newTabPageManager } - func perform() { - guard appSettings.newTabPageIntroMessageEnabled == nil else { return } + func perform(ignoringPublicAvailabilityCheck ignorePublicCheck: Bool = false) { + let isPublicOrBypassed = ignorePublicCheck || newTabPageManager.isAvailableInPublicRelease + let isNotSetUp = appSettings.newTabPageIntroMessageEnabled == nil + + guard isPublicOrBypassed && isNotSetUp else { return } // For new users we **don't** want intro message appSettings.newTabPageIntroMessageEnabled = statistics.installDate != nil diff --git a/DuckDuckGo/NewTabPageManager.swift b/DuckDuckGo/NewTabPageManager.swift index 0b1ff35c8e..1d4d39b723 100644 --- a/DuckDuckGo/NewTabPageManager.swift +++ b/DuckDuckGo/NewTabPageManager.swift @@ -19,9 +19,11 @@ import Foundation import BrowserServicesKit +import Core protocol NewTabPageManaging: AnyObject { var isNewTabPageSectionsEnabled: Bool { get } + var isAvailableInPublicRelease: Bool { get } } protocol NewTabPageDebugging: NewTabPageManaging { @@ -47,6 +49,15 @@ final class NewTabPageManager: NewTabPageManaging, NewTabPageDebugging { isLocalFlagEnabled && isFeatureFlagEnabled } + var isAvailableInPublicRelease: Bool { + switch FeatureFlag.newTabPageSections.source { + case .disabled, .internalOnly, .remoteDevelopment: + return false + case .remoteReleasable: + return true + } + } + // MARK: - NewTabPageDebugging var isLocalFlagEnabled: Bool { @@ -55,6 +66,9 @@ final class NewTabPageManager: NewTabPageManaging, NewTabPageDebugging { } set { appDefaults.newTabPageSectionsEnabled = newValue + if newValue { + NewTabPageIntroMessageSetup().perform(ignoringPublicAvailabilityCheck: true) + } } } diff --git a/DuckDuckGo/NewTabPageSectionsDebugView.swift b/DuckDuckGo/NewTabPageSectionsDebugView.swift index 1e568c2934..62c0142e4e 100644 --- a/DuckDuckGo/NewTabPageSectionsDebugView.swift +++ b/DuckDuckGo/NewTabPageSectionsDebugView.swift @@ -26,13 +26,15 @@ struct NewTabPageSectionsDebugView: View { @State private var isFeatureEnabled: Bool @State private var introMessageCount: Int - + @State private var isIntroMessageInitialized: Bool + private var localFlagEnabled: Binding { Binding { newTabPageDebugging.isLocalFlagEnabled } set: { newTabPageDebugging.isLocalFlagEnabled = $0 isFeatureEnabled = newTabPageDebugging.isNewTabPageSectionsEnabled + isIntroMessageInitialized = appSettings.newTabPageIntroMessageEnabled != nil } } @@ -41,6 +43,7 @@ struct NewTabPageSectionsDebugView: View { appSettings.newTabPageIntroMessageEnabled ?? false } set: { appSettings.newTabPageIntroMessageEnabled = $0 + isIntroMessageInitialized = appSettings.newTabPageIntroMessageEnabled != nil } } @@ -60,13 +63,25 @@ struct NewTabPageSectionsDebugView: View { appSettings = AppDependencyProvider.shared.appSettings introMessageCount = appSettings.newTabPageIntroMessageSeenCount + isIntroMessageInitialized = appSettings.newTabPageIntroMessageEnabled != nil } var body: some View { List { + Section { + Toggle(isOn: localFlagEnabled, + label: { + Text(verbatim: "Local setting enabled") + }) + } header: { + Text(verbatim: "Feature settings") + } footer: { + Text(verbatim: "Requires internal user flag set to have an effect.\n\nEnabling the local flag will cause existing-user behavior for feature Intro Message.") + } + Section { HStack { - Text("New tab page sections enabled") + Text(verbatim: "New tab page sections enabled") Spacer() if isFeatureEnabled { Image(systemName: "checkmark") @@ -76,11 +91,11 @@ struct NewTabPageSectionsDebugView: View { .foregroundColor(Color.red40) } } - } - - Section { + HStack { - Text("Feature flag enabled") + VStack { + Text(verbatim: "Feature flag enabled") + } Spacer() if newTabPageDebugging.isFeatureFlagEnabled { Image(systemName: "checkmark") @@ -92,33 +107,38 @@ struct NewTabPageSectionsDebugView: View { .foregroundColor(Color.red40) } } - } footer: { - Text("Requires internal user") } - - Section { - Toggle(isOn: localFlagEnabled, - label: { - Text("Local setting enabled") - }) - } - + Section { + HStack { + Text(verbatim: "Intro message initialized") + Spacer() + Text(verbatim: isIntroMessageInitialized.description.localizedCapitalized) + .frame(alignment: .trailing) + .foregroundStyle(.secondary) + } + Toggle(isOn: introMessageEnabled) { - Text("Intro message") + Text(verbatim: "Intro message") } + HStack { - Text("Message seen count") - .frame(maxWidth: .infinity, alignment: .leading) - Text("\(introMessageCount)") + Text(verbatim: "Message seen count") + Spacer() + Text(verbatim: "\(introMessageCount)") .frame(alignment: .trailing) .foregroundStyle(.secondary) } Button("Reset message seen count", action: { introMessageCountBinding.wrappedValue = 0 }) + + Button("Reset intro message setting", action: { + appSettings.newTabPageIntroMessageEnabled = nil + isIntroMessageInitialized = false + }) } header: { - Text("Other Settings") + Text(verbatim: "Intro message") } } .applyInsetGroupedListStyle() diff --git a/DuckDuckGoTests/NewTabPageIntroMessageSetupTests.swift b/DuckDuckGoTests/NewTabPageIntroMessageSetupTests.swift index 8a235c2793..a47829b6ea 100644 --- a/DuckDuckGoTests/NewTabPageIntroMessageSetupTests.swift +++ b/DuckDuckGoTests/NewTabPageIntroMessageSetupTests.swift @@ -22,11 +22,12 @@ import XCTest final class NewTabPageIntroMessageSetupTests: XCTestCase { - let appSettings = AppSettingsMock() - let statistics = MockStatisticsStore() + private let appSettings = AppSettingsMock() + private let statistics = MockStatisticsStore() + private let ntpManagerMock = NewTabPageManagerMock() func testEnablesFeatureForExistingUser() { - let sut = NewTabPageIntroMessageSetup(appSettings: appSettings, statistics: statistics) + let sut = createSUT() statistics.installDate = Date() sut.perform() @@ -35,7 +36,7 @@ final class NewTabPageIntroMessageSetupTests: XCTestCase { } func testDisablesFeatureForNewUser() { - let sut = NewTabPageIntroMessageSetup(appSettings: appSettings, statistics: statistics) + let sut = createSUT() statistics.installDate = nil sut.perform() @@ -44,7 +45,7 @@ final class NewTabPageIntroMessageSetupTests: XCTestCase { } func testDoesNothingIfSetAlready() { - let sut = NewTabPageIntroMessageSetup(appSettings: appSettings, statistics: statistics) + let sut = createSUT() statistics.installDate = nil appSettings.newTabPageIntroMessageEnabled = true @@ -52,4 +53,35 @@ final class NewTabPageIntroMessageSetupTests: XCTestCase { XCTAssertEqual(appSettings.newTabPageIntroMessageEnabled, true) } + + func testDoesNothingIfNotPubliclyReleased() { + let sut = createSUT() + statistics.installDate = nil + ntpManagerMock.isAvailableInPublicRelease = false + appSettings.newTabPageIntroMessageEnabled = nil + + sut.perform() + + XCTAssertNil(appSettings.newTabPageIntroMessageEnabled) + } + + func testPerformsSetupWhenPublicReleaseBypassed() { + let sut = createSUT() + statistics.installDate = nil + ntpManagerMock.isAvailableInPublicRelease = false + appSettings.newTabPageIntroMessageEnabled = nil + + sut.perform(ignoringPublicAvailabilityCheck: true) + + XCTAssertNotNil(appSettings.newTabPageIntroMessageEnabled) + } + + private func createSUT() -> NewTabPageIntroMessageSetup { + NewTabPageIntroMessageSetup(appSettings: appSettings, statistics: statistics, newTabPageManager: ntpManagerMock) + } +} + +private final class NewTabPageManagerMock: NewTabPageManaging { + var isNewTabPageSectionsEnabled: Bool = true + var isAvailableInPublicRelease: Bool = true }