Skip to content

Commit

Permalink
Merge branch 'main' into diego/remove-waitlist-code
Browse files Browse the repository at this point in the history
# By Daniel Bernal (2) and others
# Via GitHub
* main:
  Privacy Pro Follow ups (#2882)
  Clean up subscriptionFeature on init (#2880)
  apply user agent overrides to downloads (#2883)
  Add `survey` action and Privacy Pro attributes (#2879)

# Conflicts:
#	DuckDuckGo.xcodeproj/project.pbxproj
#	DuckDuckGo.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
#	DuckDuckGo/RemoteMessaging.swift
#	DuckDuckGoTests/RemoteMessagingStoreTests.swift
  • Loading branch information
samsymons committed May 24, 2024
2 parents 2cb2367 + 347c408 commit d2ee345
Show file tree
Hide file tree
Showing 14 changed files with 63 additions and 67 deletions.
6 changes: 6 additions & 0 deletions Core/UserAgentManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ public protocol UserAgentManager {

func userAgent(isDesktop: Bool) -> String

func userAgent(isDesktop: Bool, url: URL?) -> String

}

public class DefaultUserAgentManager: UserAgentManager {
Expand Down Expand Up @@ -60,6 +62,10 @@ public class DefaultUserAgentManager: UserAgentManager {
return userAgent.agent(forUrl: nil, isDesktop: isDesktop)
}

public func userAgent(isDesktop: Bool, url: URL?) -> String {
return userAgent.agent(forUrl: url, isDesktop: isDesktop)
}

public func update(request: inout URLRequest, isDesktop: Bool) {
request.addValue(userAgent.agent(forUrl: nil, isDesktop: isDesktop), forHTTPHeaderField: "User-Agent")
}
Expand Down
7 changes: 4 additions & 3 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@
4B6484F327FD1E350050A7A1 /* MenuControllerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B6484E927FD1E340050A7A1 /* MenuControllerView.swift */; };
4B6ED9452B992FE4007F5CAA /* vpn-dark-mode.json in Resources */ = {isa = PBXBuildFile; fileRef = 4B6ED9442B992FE4007F5CAA /* vpn-dark-mode.json */; };
4B75EA9226A266CB00018634 /* PrintingUserScript.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B75EA9126A266CB00018634 /* PrintingUserScript.swift */; };
4B78074E2B183A1F009DB2CF /* SurveyURLBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B78074D2B183A1F009DB2CF /* SurveyURLBuilder.swift */; };
4B78074E2B183A1F009DB2CF /* RemoteMessagingSurveyURLBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B78074D2B183A1F009DB2CF /* RemoteMessagingSurveyURLBuilder.swift */; };
4B948E2629DCCDB9002531FA /* Persistence in Frameworks */ = {isa = PBXBuildFile; productRef = 4B948E2529DCCDB9002531FA /* Persistence */; };
4BB7CBB02AF59C310014A35F /* VPNWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB7CBAF2AF59C310014A35F /* VPNWidget.swift */; };
4BBBBA872B02E85400D965DA /* DesignResourcesKit in Frameworks */ = {isa = PBXBuildFile; productRef = 4BBBBA862B02E85400D965DA /* DesignResourcesKit */; };
Expand Down Expand Up @@ -1300,7 +1300,7 @@
4B6484E927FD1E340050A7A1 /* MenuControllerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MenuControllerView.swift; sourceTree = "<group>"; };
4B6ED9442B992FE4007F5CAA /* vpn-dark-mode.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = "vpn-dark-mode.json"; sourceTree = "<group>"; };
4B75EA9126A266CB00018634 /* PrintingUserScript.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrintingUserScript.swift; sourceTree = "<group>"; };
4B78074D2B183A1F009DB2CF /* SurveyURLBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SurveyURLBuilder.swift; sourceTree = "<group>"; };
4B78074D2B183A1F009DB2CF /* RemoteMessagingSurveyURLBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteMessagingSurveyURLBuilder.swift; sourceTree = "<group>"; };
4BB7CBAF2AF59C310014A35F /* VPNWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNWidget.swift; sourceTree = "<group>"; };
4BBBBA912B03291700D965DA /* VPNWaitlistUserText.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VPNWaitlistUserText.swift; sourceTree = "<group>"; };
4BC21A2C272388BD00229F0E /* RunLoopExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunLoopExtensionTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4292,6 +4292,7 @@
C1B7B52028941F2A0098FD6A /* RemoteMessageRequest.swift */,
C1B7B52128941F2A0098FD6A /* RemoteMessaging.swift */,
C1B7B51F28941F2A0098FD6A /* RemoteMessagingStore.swift */,
4B78074D2B183A1F009DB2CF /* RemoteMessagingSurveyURLBuilder.swift */,
);
name = RemoteMessaging;
sourceTree = "<group>";
Expand Down Expand Up @@ -6407,7 +6408,7 @@
F1386BA41E6846C40062FC3C /* TabDelegate.swift in Sources */,
37CF91602BB4737300BADCAE /* CrashCollectionOnboarding.swift in Sources */,
C1B924B72ACD6E6800EE7B06 /* AutofillNeverSavedTableViewCell.swift in Sources */,
4B78074E2B183A1F009DB2CF /* SurveyURLBuilder.swift in Sources */,
4B78074E2B183A1F009DB2CF /* RemoteMessagingSurveyURLBuilder.swift in Sources */,
3132FA2A27A0788F00DD7A12 /* QuickLookPreviewHelper.swift in Sources */,
D670E5BB2BB6A75300941A42 /* SubscriptionNavigationCoordinator.swift in Sources */,
C1D21E2D293A5965006E5A05 /* AutofillLoginSession.swift in Sources */,
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo/AutofillLoginSettingsListViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ final class AutofillLoginSettingsListViewController: UIViewController {
let messageView = PasswordsSurveyView(surveyButtonAction: { [weak self] in
let survey = "https://selfserve.decipherinc.com/survey/selfserve/32ab/240409"
if let surveyURL = URL(string: survey) {
let surveyURLBuilder = DefaultSurveyURLBuilder()
let surveyURLBuilder = DefaultRemoteMessagingSurveyURLBuilder()
let surveyURLWithParameters = surveyURLBuilder.addPasswordsCountSurveyParameter(to: surveyURL)
LaunchTabNotification.postLaunchTabNotification(urlString: surveyURLWithParameters.absoluteString)
} else {
Expand Down
13 changes: 1 addition & 12 deletions DuckDuckGo/HomeMessageViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -139,20 +139,9 @@ struct HomeMessageViewModel {
LaunchTabNotification.postLaunchTabNotification(urlString: value)
onDidClose(buttonAction)
}
case .surveyURL(let value):
case .survey(let value):
return {
#if NETWORK_PROTECTION
if let surveyURL = URL(string: value) {
let surveyURLBuilder = DefaultSurveyURLBuilder()
let surveyURLWithParameters = surveyURLBuilder.addSurveyParameters(to: surveyURL)
LaunchTabNotification.postLaunchTabNotification(urlString: surveyURLWithParameters.absoluteString)
} else {
LaunchTabNotification.postLaunchTabNotification(urlString: value)
}
#else
LaunchTabNotification.postLaunchTabNotification(urlString: value)
#endif

onDidClose(buttonAction)
}
case .appStore:
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo/HomeMessageViewModelBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ extension RemoteAction {
case .share(let value, let title):
return .share(value: value, title: title)

case .appStore, .url, .surveyURL:
case .appStore, .url, .survey:
if isSecondaryAction {
return .cancel
}
Expand Down
17 changes: 9 additions & 8 deletions DuckDuckGo/RemoteMessaging.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import Persistence
import Bookmarks
import RemoteMessaging
import NetworkProtection
import Subscription

struct RemoteMessaging {

Expand Down Expand Up @@ -153,15 +154,12 @@ struct RemoteMessaging {
case .success(let statusResponse):
os_log("Successfully fetched remote messages", log: .remoteMessaging, type: .debug)

let daysSinceNetworkProtectionEnabled: Int
let isPrivacyProSubscriber = AppDependencyProvider.shared.subscriptionManager.accountManager.isUserAuthenticated
let canPurchase = AppDependencyProvider.shared.subscriptionManager.canPurchase

#if NETWORK_PROTECTION
let activationDateStore = DefaultVPNActivationDateStore()

daysSinceNetworkProtectionEnabled = activationDateStore.daysSinceActivation() ?? -1
#else
daysSinceNetworkProtectionEnabled = -1
#endif
let daysSinceNetworkProtectionEnabled = activationDateStore.daysSinceActivation() ?? -1
let surveyActionMapper = DefaultRemoteMessagingSurveyURLBuilder(statisticsStore: statisticsStore)

let remoteMessagingConfigMatcher = RemoteMessagingConfigMatcher(
appAttributeMatcher: AppAttributeMatcher(statisticsStore: statisticsStore,
Expand All @@ -173,8 +171,11 @@ struct RemoteMessaging {
favoritesCount: favoritesCount,
appTheme: AppUserDefaults().currentThemeName.rawValue,
isWidgetInstalled: isWidgetInstalled,
daysSinceNetPEnabled: daysSinceNetworkProtectionEnabled),
daysSinceNetPEnabled: daysSinceNetworkProtectionEnabled,
isPrivacyProEligibleUser: canPurchase,
isPrivacyProSubscriber: isPrivacyProSubscriber),
percentileStore: RemoteMessagingPercentileUserDefaultsStore(userDefaults: .standard),
surveyActionMapper: surveyActionMapper,
dismissedMessageIds: remoteMessagingStore.fetchDismissedRemoteMessageIds()
)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
//
// SurveyURLBuilder.swift
// RemoteMessagingSurveyURLBuilder.swift
// DuckDuckGo
//
// Copyright © 2023 DuckDuckGo. All rights reserved.
Expand All @@ -17,28 +17,13 @@
// limitations under the License.
//

#if NETWORK_PROTECTION

import Foundation
import BrowserServicesKit
import RemoteMessaging
import Core
import Common

protocol SurveyURLBuilder {
func addSurveyParameters(to url: URL) -> URL
}

struct DefaultSurveyURLBuilder: SurveyURLBuilder {

enum SurveyURLParameters: String, CaseIterable {
case atb = "atb"
case atbVariant = "var"
case daysSinceActivated = "delta"
case iosVersion = "mv"
case appVersion = "ddgv"
case hardwareModel = "mo"
case lastActiveDate = "da"
}
struct DefaultRemoteMessagingSurveyURLBuilder: RemoteMessagingSurveyActionMapping {

private let statisticsStore: StatisticsStore
private let activationDateStore: VPNActivationDateStore
Expand All @@ -50,15 +35,15 @@ struct DefaultSurveyURLBuilder: SurveyURLBuilder {
}

// swiftlint:disable:next cyclomatic_complexity
func addSurveyParameters(to surveyURL: URL) -> URL {
func add(parameters: [RemoteMessagingSurveyActionParameter], to surveyURL: URL) -> URL {
guard var components = URLComponents(string: surveyURL.absoluteString) else {
assertionFailure("Could not build URL components from survey URL")
return surveyURL
}

var queryItems = components.queryItems ?? []

for parameter in SurveyURLParameters.allCases {
for parameter in parameters {
switch parameter {
case .atb:
if let atb = statisticsStore.atb {
Expand All @@ -68,11 +53,7 @@ struct DefaultSurveyURLBuilder: SurveyURLBuilder {
if let variant = statisticsStore.variant {
queryItems.append(URLQueryItem(name: parameter.rawValue, value: variant))
}
case .daysSinceActivated:
if let daysSinceActivated = activationDateStore.daysSinceActivation() {
queryItems.append(URLQueryItem(name: parameter.rawValue, value: String(describing: daysSinceActivated)))
}
case .iosVersion:
case .osVersion:
queryItems.append(URLQueryItem(name: parameter.rawValue, value: AppVersion.shared.osVersion))
case .appVersion:
queryItems.append(URLQueryItem(name: parameter.rawValue, value: AppVersion.shared.versionAndBuildNumber))
Expand All @@ -83,6 +64,11 @@ struct DefaultSurveyURLBuilder: SurveyURLBuilder {
if let daysSinceLastActive = activationDateStore.daysSinceLastActive() {
queryItems.append(URLQueryItem(name: parameter.rawValue, value: String(describing: daysSinceLastActive)))
}
case .daysInstalled:
if let installDate = statisticsStore.installDate,
let daysSinceInstall = Calendar.current.numberOfDaysBetween(installDate, and: Date()) {
queryItems.append(URLQueryItem(name: parameter.rawValue, value: String(describing: daysSinceInstall)))
}
}
}

Expand All @@ -92,7 +78,7 @@ struct DefaultSurveyURLBuilder: SurveyURLBuilder {
}

func addPasswordsCountSurveyParameter(to surveyURL: URL) -> URL {
let surveyURLWithParameters = addSurveyParameters(to: surveyURL)
let surveyURLWithParameters = add(parameters: RemoteMessagingSurveyActionParameter.allCases, to: surveyURL)

guard var components = URLComponents(string: surveyURLWithParameters.absoluteString), let bucket = passwordsCountBucket() else {
return surveyURLWithParameters
Expand Down Expand Up @@ -129,5 +115,3 @@ struct DefaultSurveyURLBuilder: SurveyURLBuilder {
}

}

#endif
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ final class SubscriptionContainerViewModel: ObservableObject {
userScript: SubscriptionPagesUserScript,
subFeature: SubscriptionPagesUseSubscriptionFeature) {
self.userScript = userScript
subFeature.cleanup()
self.subFeature = subFeature
self.flow = SubscriptionFlowViewModel(origin: origin,
userScript: userScript,
Expand All @@ -49,7 +50,4 @@ final class SubscriptionContainerViewModel: ObservableObject {
subscriptionManager: subscriptionManager)
}

deinit {
subFeature.cleanup()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ final class SubscriptionFlowViewModel: ObservableObject {


subFeature.onBackToSettings = {
self.state.shouldGoBackToSettings = true
DispatchQueue.main.async {
self.state.shouldGoBackToSettings = true
}
}

subFeature.onActivateSubscription = {
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo/URLDownloadSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class URLDownloadSession: NSObject, DownloadSession {
self.session = session
} else {
let configuration = URLSessionConfiguration.ephemeral
let userAgent = DefaultUserAgentManager.shared.userAgent(isDesktop: false)
let userAgent = DefaultUserAgentManager.shared.userAgent(isDesktop: false, url: url)
configuration.httpAdditionalHeaders = ["user-agent": userAgent]
self.session = URLSession(configuration: configuration, delegate: self, delegateQueue: .main)
}
Expand Down
8 changes: 4 additions & 4 deletions DuckDuckGo/UserText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -985,7 +985,7 @@ But if you *do* want a peek under the hood, you can find more information about
// Subscription Section
public static let settingsPProSection = NSLocalizedString("settings.ppro", value: "Privacy Pro", comment: "Product name for the subscription bundle")
public static let settingsPProSubscribe = NSLocalizedString("settings.subscription.subscribe", value: "Subscribe to Privacy Pro", comment: "Call to action title for Privacy Pro")
public static let settingsPProDescription = NSLocalizedString("settings.subscription.description", value:"More seamless privacy with three new protections, including:", comment: "Privacy pro description subtext")
public static let settingsPProDescription = NSLocalizedString("settings.subscription.description", value:"More seamless privacy with three new protections:", comment: "Privacy pro description subtext")
public static let settingsPProFeatures = NSLocalizedString("settings.subscription.features", value:
"""
• VPN
Expand All @@ -1004,7 +1004,7 @@ But if you *do* want a peek under the hood, you can find more information about
public static let settingsPProITRTitle = NSLocalizedString("settings.subscription.ITR.title", value: "Identity Theft Restoration", comment: "Identity theft restoration cell title for privacy pro")
public static let settingsPProITRSubTitle = NSLocalizedString("settings.subscription.ITR.subtitle", value: "If your identity is stolen, we'll help restore it", comment: "Identity theft restoration cell subtitle for privacy pro")

public static let settingsPProActivationPendingTitle = NSLocalizedString("settings.subscription.activation.pending.title", value: "Your Subscription is Being Activated", comment: "Subscription activation pending title")
public static let settingsPProActivationPendingTitle = NSLocalizedString("settings.subscription.activation.pending.title", value: "Your Subscription is being activated", comment: "Subscription activation pending title")
public static let settingsPProActivationPendingDescription = NSLocalizedString("settings.subscription.activation.pending.description", value: "This is taking longer than usual, please check back later.", comment: "Subscription activation pending description")

// Expired Subscription
Expand Down Expand Up @@ -1122,7 +1122,7 @@ But if you *do* want a peek under the hood, you can find more information about
public static let subscriptionRestoreAddEmailTitle = NSLocalizedString("subscription.add.email.title", value: "Add Email", comment: "View title for adding email to subscription")

// Manage Subscription Email
public static let subscriptionManageEmailDescription = NSLocalizedString("subscription.manage.email.description", value: "You can use this email to activate your subscription from browser settings in the DuckDuckGo app on your other devices.", comment: "Description for Email Management options")
public static let subscriptionManageEmailDescription = NSLocalizedString("subscription.manage.email.description", value: "Use this email to activate your subscription from browser settings in the DuckDuckGo app on other devices..", comment: "Description for Email Management options")
public static let subscriptionManageEmailButton = NSLocalizedString("subscription.activate.manage.email.button", value: "Manage", comment: "Restore button title for Managing Email")
public static let subscriptionManageEmailTitle = NSLocalizedString("subscription.activate.manage.email.title", value: "Manage Email", comment: "View Title for managing your email account")
public static let subscriptionManageEmailCancelButton = NSLocalizedString("subscription.activate.manage.email.cancel", value: "Cancel", comment: "Button title for cancelling email deletion")
Expand Down Expand Up @@ -1161,7 +1161,7 @@ But if you *do* want a peek under the hood, you can find more information about
public static let subscriptionPIRHeroDesktopMenuLocation = NSLocalizedString("subscription.pir.heroTextLocation", value: "Settings > Privacy Pro", comment: "Settings references a menu in the Desktop app, Privacy Pro, references our product name")
public static let subscriptionPIRHeroDesktopMenuItem = NSLocalizedString("subscription.pir.heroTextMenyEntry", value: "I have a subscription", comment: "Menu item for enabling Personal Information Removal on Desktop")
public static let subscriptionPIRWindows = NSLocalizedString("subscription.pir.windows", value: "Windows", comment: "Text for the 'Windows' button")
public static let subscriptionPIRMacOS = NSLocalizedString("subscription.pir.macos", value: "macOS", comment: "Text for the 'macOS' button")
public static let subscriptionPIRMacOS = NSLocalizedString("subscription.pir.macos", value: "Mac", comment: "Text for the 'macOS' button")

// Autocomplete
public static let autocompleteHistoryWarningTitle = NSLocalizedString("autocomplete.history.warning.title", value: "Same privacy.\nBetter search suggestions!", comment: "Title for message show in suggestions")
Expand Down
Loading

0 comments on commit d2ee345

Please sign in to comment.