Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Increase ratio of complete form saves #3698

Merged
merged 15 commits into from
Dec 9, 2024
3 changes: 3 additions & 0 deletions Core/FeatureFlag.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public enum FeatureFlag: String {
case autofillFailureReporting
case autofillOnForExistingUsers
case autofillUnknownUsernameCategorization
case autofillPartialFormSaves
case incontextSignup
case autoconsentOnByDefault
case history
Expand Down Expand Up @@ -101,6 +102,8 @@ extension FeatureFlag: FeatureFlagDescribing {
return .remoteReleasable(.subfeature(AutofillSubfeature.onForExistingUsers))
case .autofillUnknownUsernameCategorization:
return .remoteReleasable(.subfeature(AutofillSubfeature.unknownUsernameCategorization))
case .autofillPartialFormSaves:
return .remoteReleasable(.subfeature(AutofillSubfeature.partialFormSaves))
case .incontextSignup:
return .remoteReleasable(.feature(.incontextSignup))
case .autoconsentOnByDefault:
Expand Down
1 change: 1 addition & 0 deletions Core/Pixel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,7 @@

// Autofill
public static let countBucket = "count_bucket"
public static let backfilled = "backfilled"

// Privacy Dashboard
public static let daysSinceInstall = "daysSinceInstall"
Expand Down Expand Up @@ -271,7 +272,7 @@
allowedQueryReservedCharacters: allowedQueryReservedCharacters,
headers: headers)
let request = APIRequest(configuration: configuration, urlSession: .session(useMainThreadCallbackQueue: true))
request.fetch { _, error in

Check warning on line 275 in Core/Pixel.swift

View workflow job for this annotation

GitHub Actions / Make Release Build

'fetch(completion:)' is deprecated: Please use 'APIService' instead.

Check warning on line 275 in Core/Pixel.swift

View workflow job for this annotation

GitHub Actions / Make Release Build

'fetch(completion:)' is deprecated: Please use 'APIService' instead.
Logger.general.debug("Pixel fired \(pixelName, privacy: .public) \(params, privacy: .public)")
onComplete(error)
}
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11372,7 +11372,7 @@
repositoryURL = "https://github.com/DuckDuckGo/BrowserServicesKit";
requirement = {
kind = exactVersion;
version = 218.1.0;
version = 219.0.0;
};
};
9F8FE9472BAE50E50071E372 /* XCRemoteSwiftPackageReference "lottie-spm" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/DuckDuckGo/BrowserServicesKit",
"state" : {
"revision" : "a82c14b991f8cbef46d4b7d8e613762f1592fcd2",
"version" : "218.1.0"
"revision" : "20df9e22d5a69acfc25204fb0228a9d6f79b721f",
"version" : "219.0.0"
}
},
{
Expand Down
1 change: 1 addition & 0 deletions DuckDuckGo/EmailSignupViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ class EmailSignupViewController: UIViewController {

lazy private var vaultManager: SecureVaultManager = {
let manager = SecureVaultManager(includePartialAccountMatches: true,
shouldAllowPartialFormSaves: featureFlagger.isFeatureOn(.autofillPartialFormSaves),
tld: AppDependencyProvider.shared.storageCache.tld)
manager.delegate = self
return manager
Expand Down
13 changes: 11 additions & 2 deletions DuckDuckGo/SaveAutofillLoginManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ protocol SaveAutofillLoginManagerProtocol {
var isPasswordOnlyAccount: Bool { get }
var hasOtherCredentialsOnSameDomain: Bool { get }
var hasSavedMatchingPasswordWithoutUsername: Bool { get }
var hasSavedMatchingUsernameWithoutPassword: Bool { get }
var hasSavedMatchingUsername: Bool { get }

static func saveCredentials(_ credentials: SecureVaultModels.WebsiteCredentials, with factory: AutofillVaultFactory) throws -> Int64
Expand Down Expand Up @@ -85,7 +86,15 @@ final class SaveAutofillLoginManager: SaveAutofillLoginManagerProtocol {
var hasSavedMatchingUsername: Bool {
savedMatchingUsernameCredential != nil
}


var hasSavedMatchingUsernameWithoutPassword: Bool {
if let savedMatchingUsernameCredential {
return savedMatchingUsernameCredential.password.flatMap { String(data: $0, encoding: .utf8) }.isNilOrEmpty
} else {
return false
}
}

func prepareData(completion: @escaping () -> Void) {
fetchDomainStoredCredentials { [weak self] credentials in
self?.domainStoredCredentials = credentials
Expand All @@ -112,7 +121,7 @@ final class SaveAutofillLoginManager: SaveAutofillLoginManagerProtocol {
let credentialsWithSameUsername = domainStoredCredentials.filter { $0.account.username == credentials.account.username }
return credentialsWithSameUsername.first
}

private func fetchDomainStoredCredentials(completion: @escaping ([SecureVaultModels.WebsiteCredentials]) -> Void) {
DispatchQueue.global(qos: .userInteractive).async {
var result = [SecureVaultModels.WebsiteCredentials]()
Expand Down
1 change: 1 addition & 0 deletions DuckDuckGo/SaveLoginView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ private enum Const {

struct SaveLoginView_Previews: PreviewProvider {
private struct MockManager: SaveAutofillLoginManagerProtocol {
var hasSavedMatchingUsernameWithoutPassword: Bool { false }

var username: String { "[email protected]" }
var visiblePassword: String { "supersecurepasswordquack" }
Expand Down
18 changes: 12 additions & 6 deletions DuckDuckGo/SaveLoginViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,11 @@ class SaveLoginViewController: UIViewController {
case .savePassword:
Pixel.fire(pixel: .autofillLoginsSavePasswordModalDismissed)
case .updateUsername:
Pixel.fire(pixel: .autofillLoginsUpdateUsernameModalDismissed)
let isBackfilled = viewModel.isUpdatingEmptyUsername
Pixel.fire(pixel: .autofillLoginsUpdateUsernameModalDismissed, withAdditionalParameters: [PixelParameters.backfilled: String(describing: isBackfilled)])
case .updatePassword:
Pixel.fire(pixel: .autofillLoginsUpdatePasswordModalDismissed)
let isBackfilled = viewModel.isUpdatingEmptyPassword
Pixel.fire(pixel: .autofillLoginsUpdatePasswordModalDismissed, withAdditionalParameters: [PixelParameters.backfilled: String(describing: isBackfilled)])
}

viewModel.viewControllerDidDisappear()
Expand All @@ -103,9 +105,11 @@ class SaveLoginViewController: UIViewController {
case .savePassword:
Pixel.fire(pixel: .autofillLoginsSavePasswordModalDisplayed)
case .updateUsername:
Pixel.fire(pixel: .autofillLoginsUpdateUsernameModalDisplayed)
let isBackfilled = saveViewModel.isUpdatingEmptyUsername
Pixel.fire(pixel: .autofillLoginsUpdateUsernameModalDisplayed, withAdditionalParameters: [PixelParameters.backfilled: String(describing: isBackfilled)])
case .updatePassword:
Pixel.fire(pixel: .autofillLoginsUpdatePasswordModalDisplayed)
let isBackfilled = saveViewModel.isUpdatingEmptyPassword
Pixel.fire(pixel: .autofillLoginsUpdatePasswordModalDisplayed, withAdditionalParameters: [PixelParameters.backfilled: String(describing: isBackfilled)])
}
}
}
Expand All @@ -124,9 +128,11 @@ extension SaveLoginViewController: SaveLoginViewModelDelegate {
delegate?.saveLoginViewController(self, didSaveCredentials: credentialManager.credentials)
case .updatePassword, .updateUsername:
if viewModel.layoutType == .updatePassword {
Pixel.fire(pixel: .autofillLoginsUpdatePasswordModalConfirmed)
let isBackfilled = viewModel.isUpdatingEmptyPassword
Pixel.fire(pixel: .autofillLoginsUpdatePasswordModalConfirmed, withAdditionalParameters: [PixelParameters.backfilled: String(describing: isBackfilled)])
} else {
Pixel.fire(pixel: .autofillLoginsUpdateUsernameModalConfirmed)
let isBackfilled = viewModel.isUpdatingEmptyUsername
Pixel.fire(pixel: .autofillLoginsUpdateUsernameModalConfirmed, withAdditionalParameters: [PixelParameters.backfilled: String(describing: isBackfilled)])
}
delegate?.saveLoginViewController(self, didUpdateCredentials: credentialManager.credentials)
}
Expand Down
8 changes: 6 additions & 2 deletions DuckDuckGo/SaveLoginViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,14 @@ final class SaveLoginViewModel: ObservableObject {
credentialManager.hasSavedMatchingUsername
}

var isUpdatingUsername: Bool {
var isUpdatingEmptyUsername: Bool {
credentialManager.hasSavedMatchingPasswordWithoutUsername
}

var isUpdatingEmptyPassword: Bool {
credentialManager.hasSavedMatchingUsernameWithoutPassword
}

var hiddenPassword: String {
PasswordHider(password: credentialManager.visiblePassword).hiddenPassword
}
Expand Down Expand Up @@ -132,7 +136,7 @@ final class SaveLoginViewModel: ObservableObject {
return .savePassword
}

if isUpdatingUsername {
if isUpdatingEmptyUsername {
return .updateUsername
}

Expand Down
1 change: 1 addition & 0 deletions DuckDuckGo/TabViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ class TabViewController: UIViewController {

lazy var vaultManager: SecureVaultManager = {
let manager = SecureVaultManager(includePartialAccountMatches: true,
shouldAllowPartialFormSaves: featureFlagger.isFeatureOn(.autofillPartialFormSaves),
tld: AppDependencyProvider.shared.storageCache.tld)
manager.delegate = self
return manager
Expand Down
Loading