Skip to content

Commit

Permalink
Remove SubscriptionFeatureAvailability from AppDependencyProvider (
Browse files Browse the repository at this point in the history
…#3447)

Task/Issue URL: https://app.asana.com/0/414709148257752/1208561301723448/f
Tech Design URL:
CC:

Description:

This PR removes SubscriptionFeatureAvailability from AppDependencyProvider. Now it instead stores the feature availability instance on the app delegate, and passes it down appropriately, such as through the MainViewController.
  • Loading branch information
samsymons authored Oct 17, 2024
1 parent 89baf51 commit 4ecadd6
Show file tree
Hide file tree
Showing 19 changed files with 81 additions and 48 deletions.
8 changes: 7 additions & 1 deletion DuckDuckGo/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ import os.log
private var autofillPixelReporter: AutofillPixelReporter?
private var autofillUsageMonitor = AutofillUsageMonitor()

private(set) var subscriptionFeatureAvailability: SubscriptionFeatureAvailability!
var privacyProDataReporter: PrivacyProDataReporting!

// MARK: lifecycle
Expand Down Expand Up @@ -302,6 +303,10 @@ import os.log
)
remoteMessagingClient.registerBackgroundRefreshTaskHandler()

subscriptionFeatureAvailability = DefaultSubscriptionFeatureAvailability(
privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager,
purchasePlatform: .appStore)

homePageConfiguration = HomePageConfiguration(variantManager: AppDependencyProvider.shared.variantManager,
remoteMessagingClient: remoteMessagingClient,
privacyProDataReporter: privacyProDataReporter)
Expand Down Expand Up @@ -336,7 +341,8 @@ import os.log
variantManager: variantManager,
contextualOnboardingPresenter: contextualOnboardingPresenter,
contextualOnboardingLogic: daxDialogs,
contextualOnboardingPixelReporter: onboardingPixelReporter)
contextualOnboardingPixelReporter: onboardingPixelReporter,
subscriptionFeatureAvailability: subscriptionFeatureAvailability)

main.loadViewIfNeeded()
syncErrorHandler.alertPresenter = main
Expand Down
8 changes: 0 additions & 8 deletions DuckDuckGo/AppDependencyProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ protocol DependencyProvider {
var configurationManager: ConfigurationManager { get }
var configurationStore: ConfigurationStore { get }
var userBehaviorMonitor: UserBehaviorMonitor { get }
var subscriptionFeatureAvailability: SubscriptionFeatureAvailability { get }
var subscriptionManager: SubscriptionManager { get }
var accountManager: AccountManager { get }
var vpnFeatureVisibility: DefaultNetworkProtectionVisibility { get }
Expand Down Expand Up @@ -75,10 +74,6 @@ final class AppDependencyProvider: DependencyProvider {

let userBehaviorMonitor = UserBehaviorMonitor()

let subscriptionFeatureAvailability: SubscriptionFeatureAvailability = DefaultSubscriptionFeatureAvailability(
privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager,
purchasePlatform: .appStore)

// Subscription
let subscriptionManager: SubscriptionManager
var accountManager: AccountManager {
Expand Down Expand Up @@ -125,9 +120,6 @@ final class AppDependencyProvider: DependencyProvider {

self.subscriptionManager = subscriptionManager

let subscriptionFeatureAvailability: SubscriptionFeatureAvailability = DefaultSubscriptionFeatureAvailability(
privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager,
purchasePlatform: .appStore)
let accessTokenProvider: () -> String? = {
return { accountManager.accessToken }
}()
Expand Down
1 change: 1 addition & 0 deletions DuckDuckGo/MainViewController+Segues.swift
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,7 @@ extension MainViewController {

let settingsViewModel = SettingsViewModel(legacyViewProvider: legacyViewProvider,
subscriptionManager: AppDependencyProvider.shared.subscriptionManager,
subscriptionFeatureAvailability: subscriptionFeatureAvailability,
deepLink: deepLinkTarget,
historyManager: historyManager,
syncPausedStateManager: syncPausedStateManager,
Expand Down
5 changes: 4 additions & 1 deletion DuckDuckGo/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ class MainViewController: UIViewController {
private var vpnCancellables = Set<AnyCancellable>()
private var feedbackCancellable: AnyCancellable?

let subscriptionFeatureAvailability: SubscriptionFeatureAvailability
let privacyProDataReporter: PrivacyProDataReporting

private lazy var featureFlagger = AppDependencyProvider.shared.featureFlagger
Expand Down Expand Up @@ -195,7 +196,8 @@ class MainViewController: UIViewController {
contextualOnboardingLogic: ContextualOnboardingLogic,
contextualOnboardingPixelReporter: OnboardingPixelReporting,
tutorialSettings: TutorialSettings = DefaultTutorialSettings(),
statisticsStore: StatisticsStore = StatisticsUserDefaults()
statisticsStore: StatisticsStore = StatisticsUserDefaults(),
subscriptionFeatureAvailability: SubscriptionFeatureAvailability
) {
self.bookmarksDatabase = bookmarksDatabase
self.bookmarksDatabaseCleaner = bookmarksDatabaseCleaner
Expand Down Expand Up @@ -226,6 +228,7 @@ class MainViewController: UIViewController {
self.contextualOnboardingLogic = contextualOnboardingLogic
self.contextualOnboardingPixelReporter = contextualOnboardingPixelReporter
self.statisticsStore = statisticsStore
self.subscriptionFeatureAvailability = subscriptionFeatureAvailability

super.init(nibName: nil, bundle: nil)

Expand Down
3 changes: 1 addition & 2 deletions DuckDuckGo/NetworkProtectionRootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@ struct NetworkProtectionRootView: View {

init() {
let accountManager = AppDependencyProvider.shared.subscriptionManager.accountManager
let subscriptionFeatureAvailability = AppDependencyProvider.shared.subscriptionFeatureAvailability
let locationListRepository = NetworkProtectionLocationListCompositeRepository(accountManager: accountManager)
let usesUnifiedFeedbackForm = accountManager.isUserAuthenticated && subscriptionFeatureAvailability.usesUnifiedFeedbackForm
let usesUnifiedFeedbackForm = accountManager.isUserAuthenticated
statusViewModel = NetworkProtectionStatusViewModel(tunnelController: AppDependencyProvider.shared.networkProtectionTunnelController,
settings: AppDependencyProvider.shared.vpnSettings,
statusObserver: AppDependencyProvider.shared.connectionObserver,
Expand Down
2 changes: 2 additions & 0 deletions DuckDuckGo/SettingsRootView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,14 @@ struct SettingsRootView: View {
SubscriptionContainerViewFactory.makeSubscribeFlow(origin: origin,
navigationCoordinator: subscriptionNavigationCoordinator,
subscriptionManager: AppDependencyProvider.shared.subscriptionManager,
subscriptionFeatureAvailability: viewModel.subscriptionFeatureAvailability,
privacyProDataReporter: viewModel.privacyProDataReporter)
.environmentObject(subscriptionNavigationCoordinator)

case .restoreFlow:
SubscriptionContainerViewFactory.makeEmailFlow(navigationCoordinator: subscriptionNavigationCoordinator,
subscriptionManager: AppDependencyProvider.shared.subscriptionManager,
subscriptionFeatureAvailability: viewModel.subscriptionFeatureAvailability,
onDisappear: {})
case .duckPlayer:
SettingsDuckPlayerView().environmentObject(viewModel)
Expand Down
31 changes: 15 additions & 16 deletions DuckDuckGo/SettingsSubscriptionView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ struct SettingsSubscriptionView: View {
static let privacyPolicyURL = URL(string: "https://duckduckgo.com/pro/privacy-terms")!
}

@EnvironmentObject var viewModel: SettingsViewModel
@EnvironmentObject var settingsViewModel: SettingsViewModel
@EnvironmentObject var subscriptionNavigationCoordinator: SubscriptionNavigationCoordinator
@State var isShowingDBP = false
@State var isShowingITP = false
Expand All @@ -46,7 +46,8 @@ struct SettingsSubscriptionView: View {

var subscriptionRestoreView: some View {
SubscriptionContainerViewFactory.makeRestoreFlow(navigationCoordinator: subscriptionNavigationCoordinator,
subscriptionManager: subscriptionManager)
subscriptionManager: subscriptionManager,
subscriptionFeatureAvailability: settingsViewModel.subscriptionFeatureAvailability)
}

private var manageSubscriptionView: some View {
Expand Down Expand Up @@ -117,7 +118,7 @@ struct SettingsSubscriptionView: View {

// Renew Subscription (Expired)
let settingsView = SubscriptionSettingsView(configuration: .expired,
settingsViewModel: viewModel,
settingsViewModel: settingsViewModel,
viewPlans: {
subscriptionNavigationCoordinator.shouldPushSubscriptionWebView = true
})
Expand All @@ -138,7 +139,7 @@ struct SettingsSubscriptionView: View {

// Renew Subscription (Expired)
let settingsView = SubscriptionSettingsView(configuration: .activating,
settingsViewModel: viewModel,
settingsViewModel: settingsViewModel,
viewPlans: {
subscriptionNavigationCoordinator.shouldPushSubscriptionWebView = true
})
Expand All @@ -155,17 +156,17 @@ struct SettingsSubscriptionView: View {
@ViewBuilder
private var subscriptionDetailsView: some View {

if viewModel.state.subscription.entitlements.contains(.networkProtection) {
if settingsViewModel.state.subscription.entitlements.contains(.networkProtection) {
NavigationLink(destination: NetworkProtectionRootView(), isActive: $isShowingVPN) {
SettingsCellView(
label: UserText.settingsPProVPNTitle,
image: Image("SettingsPrivacyProVPN"),
statusIndicator: StatusIndicatorView(status: viewModel.state.networkProtectionConnected ? .on : .off)
statusIndicator: StatusIndicatorView(status: settingsViewModel.state.networkProtectionConnected ? .on : .off)
)
}
}

if viewModel.state.subscription.entitlements.contains(.dataBrokerProtection) {
if settingsViewModel.state.subscription.entitlements.contains(.dataBrokerProtection) {
NavigationLink(destination: SubscriptionPIRView(), isActive: $isShowingDBP) {
SettingsCellView(
label: UserText.settingsPProDBPTitle,
Expand All @@ -175,7 +176,7 @@ struct SettingsSubscriptionView: View {
}
}

if viewModel.state.subscription.entitlements.contains(.identityTheftRestoration) {
if settingsViewModel.state.subscription.entitlements.contains(.identityTheftRestoration) {
NavigationLink(
destination: SubscriptionITPView(),
isActive: $isShowingITP) {
Expand All @@ -187,10 +188,8 @@ struct SettingsSubscriptionView: View {
}
}

NavigationLink(
destination: SubscriptionSettingsView(configuration: .subscribed,
settingsViewModel: viewModel)
.environmentObject(subscriptionNavigationCoordinator)
NavigationLink(destination: SubscriptionSettingsView(configuration: .subscribed, settingsViewModel: settingsViewModel)
.environmentObject(subscriptionNavigationCoordinator)
) {
SettingsCustomCell(content: { manageSubscriptionView })
}
Expand All @@ -200,9 +199,9 @@ struct SettingsSubscriptionView: View {
Group {
if isShowingPrivacyPro {

let isSignedIn = viewModel.state.subscription.isSignedIn
let hasActiveSubscription = viewModel.state.subscription.hasActiveSubscription
let hasNoEntitlements = viewModel.state.subscription.entitlements.isEmpty
let isSignedIn = settingsViewModel.state.subscription.isSignedIn
let hasActiveSubscription = settingsViewModel.state.subscription.hasActiveSubscription
let hasNoEntitlements = settingsViewModel.state.subscription.entitlements.isEmpty

let footerLink = Link(UserText.settingsPProSectionFooter,
destination: ViewConstants.privacyPolicyURL)
Expand Down Expand Up @@ -239,7 +238,7 @@ struct SettingsSubscriptionView: View {
}
}
}
.onReceive(viewModel.$state) { state in
.onReceive(settingsViewModel.$state) { state in
isShowingPrivacyPro = state.subscription.enabled && (state.subscription.isSignedIn || state.subscription.canPurchase)
}
}
Expand Down
6 changes: 3 additions & 3 deletions DuckDuckGo/SettingsViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,13 +39,13 @@ final class SettingsViewModel: ObservableObject {
private var legacyViewProvider: SettingsLegacyViewProvider
private lazy var versionProvider: AppVersion = AppVersion.shared
private let voiceSearchHelper: VoiceSearchHelperProtocol
private let subscriptionFeatureAvailability: SubscriptionFeatureAvailability
private let syncPausedStateManager: any SyncPausedStateManaging
var emailManager: EmailManager { EmailManager() }
private let historyManager: HistoryManaging
let privacyProDataReporter: PrivacyProDataReporting?
// Subscription Dependencies
private let subscriptionManager: SubscriptionManager
let subscriptionFeatureAvailability: SubscriptionFeatureAvailability
private var subscriptionSignOutObserver: Any?
var duckPlayerContingencyHandler: DuckPlayerContingencyHandler {
DefaultDuckPlayerContingencyHandler(privacyConfigurationManager: ContentBlocking.shared.privacyConfigurationManager)
Expand Down Expand Up @@ -362,7 +362,7 @@ final class SettingsViewModel: ObservableObject {
init(state: SettingsState? = nil,
legacyViewProvider: SettingsLegacyViewProvider,
subscriptionManager: SubscriptionManager,
subscriptionFeatureAvailability: SubscriptionFeatureAvailability = AppDependencyProvider.shared.subscriptionFeatureAvailability,
subscriptionFeatureAvailability: SubscriptionFeatureAvailability,
voiceSearchHelper: VoiceSearchHelperProtocol = AppDependencyProvider.shared.voiceSearchHelper,
variantManager: VariantManager = AppDependencyProvider.shared.variantManager,
deepLink: SettingsDeepLinkSection? = nil,
Expand Down Expand Up @@ -706,7 +706,7 @@ extension SettingsViewModel {
}

// Update visibility based on Feature flag
state.subscription.enabled = AppDependencyProvider.shared.subscriptionFeatureAvailability.isFeatureAvailable
state.subscription.enabled = subscriptionFeatureAvailability.isFeatureAvailable

// Update if can purchase based on App Store product availability
state.subscription.canPurchase = subscriptionManager.canPurchase
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,19 +90,22 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec

private let subscriptionAttributionOrigin: String?
private let subscriptionManager: SubscriptionManager
private let subscriptionFeatureAvailability: SubscriptionFeatureAvailability
private var accountManager: AccountManager { subscriptionManager.accountManager }
private let appStorePurchaseFlow: AppStorePurchaseFlow
private let appStoreRestoreFlow: AppStoreRestoreFlow
private let appStoreAccountManagementFlow: AppStoreAccountManagementFlow
private let privacyProDataReporter: PrivacyProDataReporting?

init(subscriptionManager: SubscriptionManager,
subscriptionFeatureAvailability: SubscriptionFeatureAvailability,
subscriptionAttributionOrigin: String?,
appStorePurchaseFlow: AppStorePurchaseFlow,
appStoreRestoreFlow: AppStoreRestoreFlow,
appStoreAccountManagementFlow: AppStoreAccountManagementFlow,
privacyProDataReporter: PrivacyProDataReporting? = nil) {
self.subscriptionManager = subscriptionManager
self.subscriptionFeatureAvailability = subscriptionFeatureAvailability
self.appStorePurchaseFlow = appStorePurchaseFlow
self.appStoreRestoreFlow = appStoreRestoreFlow
self.appStoreAccountManagementFlow = appStoreAccountManagementFlow
Expand Down Expand Up @@ -200,7 +203,7 @@ final class SubscriptionPagesUseSubscriptionFeature: Subfeature, ObservableObjec
func getSubscriptionOptions(params: Any, original: WKScriptMessage) async -> Encodable? {
resetSubscriptionFlow()
if let subscriptionOptions = await subscriptionManager.storePurchaseManager().subscriptionOptions() {
if AppDependencyProvider.shared.subscriptionFeatureAvailability.isSubscriptionPurchaseAllowed {
if subscriptionFeatureAvailability.isSubscriptionPurchaseAllowed {
return subscriptionOptions
} else {
return SubscriptionOptions.empty
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,13 +69,12 @@ final class SubscriptionSettingsViewModel: ObservableObject {

public let usesUnifiedFeedbackForm: Bool

init(subscriptionManager: SubscriptionManager = AppDependencyProvider.shared.subscriptionManager,
subscriptionFeatureAvailability: SubscriptionFeatureAvailability = AppDependencyProvider.shared.subscriptionFeatureAvailability) {
init(subscriptionManager: SubscriptionManager = AppDependencyProvider.shared.subscriptionManager) {
self.subscriptionManager = subscriptionManager
let subscriptionFAQURL = subscriptionManager.url(for: .faq)
let learnMoreURL = subscriptionFAQURL.appendingPathComponent("adding-email")
self.state = State(faqURL: subscriptionFAQURL, learnMoreURL: learnMoreURL)
self.usesUnifiedFeedbackForm = subscriptionManager.accountManager.isUserAuthenticated && subscriptionFeatureAvailability.usesUnifiedFeedbackForm
self.usesUnifiedFeedbackForm = subscriptionManager.accountManager.isUserAuthenticated

setupNotificationObservers()
}
Expand Down
Loading

0 comments on commit 4ecadd6

Please sign in to comment.