diff --git a/DuckDuckGo/OnboardingExperiment/ContextualOnboarding/ContextualDaxDialogsFactory.swift b/DuckDuckGo/OnboardingExperiment/ContextualOnboarding/ContextualDaxDialogsFactory.swift index 2c474d1541..fb14bad126 100644 --- a/DuckDuckGo/OnboardingExperiment/ContextualOnboarding/ContextualDaxDialogsFactory.swift +++ b/DuckDuckGo/OnboardingExperiment/ContextualOnboarding/ContextualDaxDialogsFactory.swift @@ -38,7 +38,7 @@ typealias ContextualOnboardingDelegate = OnboardingNavigationDelegate & Contextu // MARK: - Contextual Dialogs Factory protocol ContextualDaxDialogsFactory { - func makeView(for spec: DaxDialogs.BrowsingSpec, delegate: ContextualOnboardingDelegate) -> UIHostingController + func makeView(for spec: DaxDialogs.BrowsingSpec, delegate: ContextualOnboardingDelegate, onSizeUpdate: @escaping () -> Void) -> UIHostingController } final class ExperimentContextualDaxDialogsFactory: ContextualDaxDialogsFactory { @@ -48,15 +48,28 @@ final class ExperimentContextualDaxDialogsFactory: ContextualDaxDialogsFactory { self.contextualOnboardingSettings = contextualOnboardingSettings } - func makeView(for spec: DaxDialogs.BrowsingSpec, delegate: ContextualOnboardingDelegate) -> UIHostingController { + func makeView(for spec: DaxDialogs.BrowsingSpec, delegate: ContextualOnboardingDelegate, onSizeUpdate: @escaping () -> Void) -> UIHostingController { let rootView: AnyView switch spec.type { case .afterSearch: - rootView = AnyView(afterSearchDialog(shouldFollowUpToWebsiteSearch: !contextualOnboardingSettings.userHasSeenTrackersDialog, delegate: delegate)) + rootView = AnyView( + afterSearchDialog( + shouldFollowUpToWebsiteSearch: !contextualOnboardingSettings.userHasSeenTrackersDialog, + delegate: delegate, + onSizeUpdate: onSizeUpdate + ) + ) case .visitWebsite: rootView = AnyView(tryVisitingSiteDialog(delegate: delegate)) case .siteIsMajorTracker, .siteOwnedByMajorTracker, .withMultipleTrackers, .withOneTracker, .withoutTrackers: - rootView = AnyView(withTrackersDialog(for: spec, shouldFollowUpToFireDialog: !contextualOnboardingSettings.userHasSeenFireDialog, delegate: delegate)) + rootView = AnyView( + withTrackersDialog( + for: spec, + shouldFollowUpToFireDialog: !contextualOnboardingSettings.userHasSeenFireDialog, + delegate: delegate, + onSizeUpdate: onSizeUpdate + ) + ) case .fire: rootView = AnyView(OnboardingFireDialog()) case .final: @@ -72,11 +85,13 @@ final class ExperimentContextualDaxDialogsFactory: ContextualDaxDialogsFactory { return hostingController } - private func afterSearchDialog(shouldFollowUpToWebsiteSearch: Bool, delegate: ContextualOnboardingDelegate) -> some View { + private func afterSearchDialog(shouldFollowUpToWebsiteSearch: Bool, delegate: ContextualOnboardingDelegate, onSizeUpdate: @escaping () -> Void) -> some View { let viewModel = OnboardingSiteSuggestionsViewModel(delegate: delegate) // If should not show websites search after searching inform the delegate that the user dimissed the dialog, otherwise let the dialog handle it. + let gotItAction: () -> Void = if shouldFollowUpToWebsiteSearch { { [weak delegate] in + onSizeUpdate() delegate?.didAcknowledgeContextualOnboardingSearch() } } else { @@ -84,6 +99,7 @@ final class ExperimentContextualDaxDialogsFactory: ContextualDaxDialogsFactory { delegate?.didTapDismissContextualOnboardingAction() } } + return OnboardingFirstSearchDoneDialog(shouldFollowUp: shouldFollowUpToWebsiteSearch, viewModel: viewModel, gotItAction: gotItAction) } @@ -92,13 +108,14 @@ final class ExperimentContextualDaxDialogsFactory: ContextualDaxDialogsFactory { return OnboardingTryVisitingSiteDialog(logoPosition: .left, viewModel: viewModel) } - private func withTrackersDialog(for spec: DaxDialogs.BrowsingSpec, shouldFollowUpToFireDialog: Bool, delegate: ContextualOnboardingDelegate) -> some View { + private func withTrackersDialog(for spec: DaxDialogs.BrowsingSpec, shouldFollowUpToFireDialog: Bool, delegate: ContextualOnboardingDelegate, onSizeUpdate: @escaping () -> Void) -> some View { let attributedMessage = spec.message.attributedStringFromMarkdown(color: ThemeManager.shared.currentTheme.daxDialogTextColor) return OnboardingTrackersDoneDialog(shouldFollowUp: shouldFollowUpToFireDialog, message: attributedMessage, blockedTrackersCTAAction: { [weak self, weak delegate] in // If the user has not seen the fire dialog yet proceed to the fire dialog, otherwise dismiss the dialog. if self?.contextualOnboardingSettings.userHasSeenFireDialog == true { delegate?.didTapDismissContextualOnboardingAction() } else { + onSizeUpdate() delegate?.didAcknowledgeContextualOnboardingTrackersDialog() } }) diff --git a/DuckDuckGo/OnboardingExperiment/ContextualOnboarding/ContextualOnboardingPresenter.swift b/DuckDuckGo/OnboardingExperiment/ContextualOnboarding/ContextualOnboardingPresenter.swift index 36ac539d47..9d36907d8a 100644 --- a/DuckDuckGo/OnboardingExperiment/ContextualOnboarding/ContextualOnboardingPresenter.swift +++ b/DuckDuckGo/OnboardingExperiment/ContextualOnboarding/ContextualOnboardingPresenter.swift @@ -84,7 +84,12 @@ private extension ContextualOnboardingPresenter { ) let platformSpecificSpec = spec.withUpdatedMessage(platformSpecificMessage) // Ask the Dax Dialogs Factory for a view for the given spec - let controller = daxDialogsFactory.makeView(for: platformSpecificSpec, delegate: vc) + let controller = daxDialogsFactory.makeView(for: platformSpecificSpec, delegate: vc, onSizeUpdate: { [weak vc] in + if #unavailable(iOS 16.0) { + // For iOS 15 and below invalidate the intrinsic content size manually so the UIKit view will re-size accordingly to SwiftUI view. + vc?.daxContextualOnboardingController?.view.invalidateIntrinsicContentSize() + } + }) controller.view.isHidden = true controller.view.alpha = 0 diff --git a/DuckDuckGoTests/ContextualDaxDialogsFactoryTests.swift b/DuckDuckGoTests/ContextualDaxDialogsFactoryTests.swift index 28fb87d858..ef3da038e9 100644 --- a/DuckDuckGoTests/ContextualDaxDialogsFactoryTests.swift +++ b/DuckDuckGoTests/ContextualDaxDialogsFactoryTests.swift @@ -48,7 +48,7 @@ final class ContextualDaxDialogsFactoryTests: XCTestCase { let spec = DaxDialogs.BrowsingSpec.afterSearch // WHEN - let result = sut.makeView(for: spec, delegate: delegate) + let result = sut.makeView(for: spec, delegate: delegate, onSizeUpdate: {}) // THEN let view = try XCTUnwrap(find(OnboardingFirstSearchDoneDialog.self, in: result)) @@ -59,7 +59,7 @@ final class ContextualDaxDialogsFactoryTests: XCTestCase { // GIVEN settingsMock.userHasSeenTrackersDialog = true let spec = DaxDialogs.BrowsingSpec.afterSearch - let result = sut.makeView(for: spec, delegate: delegate) + let result = sut.makeView(for: spec, delegate: delegate, onSizeUpdate: {}) let view = try XCTUnwrap(find(OnboardingFirstSearchDoneDialog.self, in: result)) XCTAssertFalse(delegate.didCallDidTapDismissContextualOnboardingAction) @@ -75,7 +75,7 @@ final class ContextualDaxDialogsFactoryTests: XCTestCase { // GIVEN settingsMock.userHasSeenTrackersDialog = false let spec = DaxDialogs.BrowsingSpec.afterSearch - let result = sut.makeView(for: spec, delegate: delegate) + let result = sut.makeView(for: spec, delegate: delegate, onSizeUpdate: {}) let view = try XCTUnwrap(find(OnboardingFirstSearchDoneDialog.self, in: result)) XCTAssertFalse(delegate.didCallDidTapDismissContextualOnboardingAction) @@ -93,7 +93,7 @@ final class ContextualDaxDialogsFactoryTests: XCTestCase { // GIVEN settingsMock.userHasSeenTrackersDialog = true let spec = DaxDialogs.BrowsingSpec(message: "", cta: "", highlightAddressBar: false, pixelName: .onboardingIntroShownUnique, type: .visitWebsite) - let result = sut.makeView(for: spec, delegate: delegate) + let result = sut.makeView(for: spec, delegate: delegate, onSizeUpdate: {}) let view = try XCTUnwrap(find(OnboardingTryVisitingSiteDialog.self, in: result)) XCTAssertFalse(delegate.didCallDidTapDismissContextualOnboardingAction) @@ -112,7 +112,7 @@ final class ContextualDaxDialogsFactoryTests: XCTestCase { // GIVEN try [DaxDialogs.BrowsingSpec.siteIsMajorTracker, .siteOwnedByMajorTracker, .withMultipleTrackers, .withoutTrackers, .withoutTrackers].forEach { spec in // WHEN - let result = sut.makeView(for: spec, delegate: delegate) + let result = sut.makeView(for: spec, delegate: delegate, onSizeUpdate: {}) // THEN let view = try XCTUnwrap(find(OnboardingTrackersDoneDialog.self, in: result)) @@ -125,7 +125,7 @@ final class ContextualDaxDialogsFactoryTests: XCTestCase { // GIVEN delegate = ContextualOnboardingDelegateMock() settingsMock.userHasSeenFireDialog = false - let result = sut.makeView(for: spec, delegate: delegate) + let result = sut.makeView(for: spec, delegate: delegate, onSizeUpdate: {}) let view = try XCTUnwrap(find(OnboardingTrackersDoneDialog.self, in: result)) XCTAssertFalse(delegate.didCallDidAcknowledgeContextualOnboardingTrackersDialog) @@ -142,7 +142,7 @@ final class ContextualDaxDialogsFactoryTests: XCTestCase { // GIVEN delegate = ContextualOnboardingDelegateMock() settingsMock.userHasSeenFireDialog = true - let result = sut.makeView(for: spec, delegate: delegate) + let result = sut.makeView(for: spec, delegate: delegate, onSizeUpdate: {}) let view = try XCTUnwrap(find(OnboardingTrackersDoneDialog.self, in: result)) XCTAssertFalse(delegate.didCallDidTapDismissContextualOnboardingAction) @@ -160,7 +160,7 @@ final class ContextualDaxDialogsFactoryTests: XCTestCase { let spec = DaxDialogs.BrowsingSpec(message: "", cta: "", highlightAddressBar: false, pixelName: .onboardingIntroShownUnique, type: .fire) // WHEN - let result = sut.makeView(for: spec, delegate: delegate) + let result = sut.makeView(for: spec, delegate: delegate, onSizeUpdate: {}) // THEN let view = try XCTUnwrap(find(OnboardingFireDialog.self, in: result)) @@ -175,7 +175,7 @@ final class ContextualDaxDialogsFactoryTests: XCTestCase { let spec = DaxDialogs.BrowsingSpec.final // WHEN - let result = sut.makeView(for: spec, delegate: delegate) + let result = sut.makeView(for: spec, delegate: delegate, onSizeUpdate: {}) // THEN let view = try XCTUnwrap(find(OnboardingFinalDialog.self, in: result)) @@ -185,7 +185,7 @@ final class ContextualDaxDialogsFactoryTests: XCTestCase { func test_WhenCallActionOnOnboardingFinalDialog_ThenDidTapDismissContextualOnboardingActionOnDelegateIsCalled() throws { // GIVEN let spec = DaxDialogs.BrowsingSpec.final - let result = sut.makeView(for: spec, delegate: delegate) + let result = sut.makeView(for: spec, delegate: delegate, onSizeUpdate: {}) let view = try XCTUnwrap(find(OnboardingFinalDialog.self, in: result)) XCTAssertFalse(delegate.didCallDidTapDismissContextualOnboardingAction)