Skip to content

Commit

Permalink
Add autofill password modal
Browse files Browse the repository at this point in the history
  • Loading branch information
Bunn committed Oct 30, 2024
1 parent acd73c3 commit 87ed70b
Show file tree
Hide file tree
Showing 8 changed files with 254 additions and 13 deletions.
32 changes: 32 additions & 0 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,14 @@
317295D32AF058D3002C3206 /* MockWaitlistTermsAndConditionsActionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 317295D02AF058D3002C3206 /* MockWaitlistTermsAndConditionsActionHandler.swift */; };
317295D42AF058D3002C3206 /* MockWaitlistFeatureSetupHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 317295D12AF058D3002C3206 /* MockWaitlistFeatureSetupHandler.swift */; };
317295D52AF058D3002C3206 /* MockWaitlistFeatureSetupHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 317295D12AF058D3002C3206 /* MockWaitlistFeatureSetupHandler.swift */; };
317307262CD248DB00C492AB /* AutofillToolbarOnboardingPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 317307252CD248DA00C492AB /* AutofillToolbarOnboardingPopover.swift */; };
317307272CD248DB00C492AB /* AutofillToolbarOnboardingPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 317307252CD248DA00C492AB /* AutofillToolbarOnboardingPopover.swift */; };
317307292CD248EA00C492AB /* AutofillToolbarOnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 317307282CD248EA00C492AB /* AutofillToolbarOnboardingViewController.swift */; };
3173072A2CD248EA00C492AB /* AutofillToolbarOnboardingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 317307282CD248EA00C492AB /* AutofillToolbarOnboardingViewController.swift */; };
3173072C2CD2490700C492AB /* AutofillToolbarOnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3173072B2CD2490300C492AB /* AutofillToolbarOnboardingView.swift */; };
3173072D2CD2490700C492AB /* AutofillToolbarOnboardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3173072B2CD2490300C492AB /* AutofillToolbarOnboardingView.swift */; };
3173072F2CD2493900C492AB /* AutofillToolbarOnboardingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3173072E2CD2493700C492AB /* AutofillToolbarOnboardingViewModel.swift */; };
317307302CD2493900C492AB /* AutofillToolbarOnboardingViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3173072E2CD2493700C492AB /* AutofillToolbarOnboardingViewModel.swift */; };
3184AC6D288F29D800C35E4B /* BadgeNotificationAnimationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3184AC6C288F29D800C35E4B /* BadgeNotificationAnimationModel.swift */; };
3184AC6F288F2A1100C35E4B /* CookieNotificationAnimationModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3184AC6E288F2A1100C35E4B /* CookieNotificationAnimationModel.swift */; };
3199AF6F2C80734A003AEBDC /* DuckPlayerOnboardingDecider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3199AF632C80734A003AEBDC /* DuckPlayerOnboardingDecider.swift */; };
Expand Down Expand Up @@ -3398,6 +3406,10 @@
3171D6DA2889B64D0068632A /* CookieManagedNotificationContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CookieManagedNotificationContainerView.swift; sourceTree = "<group>"; };
317295D02AF058D3002C3206 /* MockWaitlistTermsAndConditionsActionHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockWaitlistTermsAndConditionsActionHandler.swift; sourceTree = "<group>"; };
317295D12AF058D3002C3206 /* MockWaitlistFeatureSetupHandler.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockWaitlistFeatureSetupHandler.swift; sourceTree = "<group>"; };
317307252CD248DA00C492AB /* AutofillToolbarOnboardingPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillToolbarOnboardingPopover.swift; sourceTree = "<group>"; };
317307282CD248EA00C492AB /* AutofillToolbarOnboardingViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillToolbarOnboardingViewController.swift; sourceTree = "<group>"; };
3173072B2CD2490300C492AB /* AutofillToolbarOnboardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillToolbarOnboardingView.swift; sourceTree = "<group>"; };
3173072E2CD2493700C492AB /* AutofillToolbarOnboardingViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillToolbarOnboardingViewModel.swift; sourceTree = "<group>"; };
3184AC6C288F29D800C35E4B /* BadgeNotificationAnimationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeNotificationAnimationModel.swift; sourceTree = "<group>"; };
3184AC6E288F2A1100C35E4B /* CookieNotificationAnimationModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CookieNotificationAnimationModel.swift; sourceTree = "<group>"; };
3192A2702A4C4E330084EA89 /* DataBrokerProtection */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = DataBrokerProtection; sourceTree = "<group>"; };
Expand Down Expand Up @@ -5327,6 +5339,17 @@
path = CookieManaged;
sourceTree = "<group>";
};
317307242CD2489900C492AB /* Onboarding */ = {
isa = PBXGroup;
children = (
3173072E2CD2493700C492AB /* AutofillToolbarOnboardingViewModel.swift */,
3173072B2CD2490300C492AB /* AutofillToolbarOnboardingView.swift */,
317307282CD248EA00C492AB /* AutofillToolbarOnboardingViewController.swift */,
317307252CD248DA00C492AB /* AutofillToolbarOnboardingPopover.swift */,
);
path = Onboarding;
sourceTree = "<group>";
};
3184AC6B288F29C600C35E4B /* BadgeAnimationContainer */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -6768,6 +6791,7 @@
7B1E819A27C8874900FF0E60 /* Autofill */ = {
isa = PBXGroup;
children = (
317307242CD2489900C492AB /* Onboarding */,
C10529482C9F45720041E502 /* Debug */,
7B1E819B27C8874900FF0E60 /* ContentOverlayPopover.swift */,
7B1E819C27C8874900FF0E60 /* ContentOverlay.storyboard */,
Expand Down Expand Up @@ -11303,6 +11327,7 @@
3706FBE1293F65D500E42796 /* AppStateRestorationManager.swift in Sources */,
9FA173EC2B7B232200EE4E6E /* AddEditBookmarkDialogView.swift in Sources */,
3706FBE2293F65D500E42796 /* ClickToLoadUserScript.swift in Sources */,
3173072C2CD2490700C492AB /* AutofillToolbarOnboardingView.swift in Sources */,
EED4D3E02C8A298D00C79EEA /* AutofillPixelEvent.swift in Sources */,
3706FBE3293F65D500E42796 /* WindowControllersManager.swift in Sources */,
37197EAA2942443D00394917 /* ModalSheetCancellable.swift in Sources */,
Expand All @@ -11313,6 +11338,7 @@
3706FBE7293F65D500E42796 /* PasswordManagementItemListModel.swift in Sources */,
3706FBE8293F65D500E42796 /* SuggestionTableCellView.swift in Sources */,
3706FBE9293F65D500E42796 /* FireViewModel.swift in Sources */,
317307292CD248EA00C492AB /* AutofillToolbarOnboardingViewController.swift in Sources */,
B68D21D02ACBC9FD002DA3C2 /* ContentBlockerRulesManagerMock.swift in Sources */,
BD7090D02C5182FB009EED82 /* UnifiedFeedbackFormView.swift in Sources */,
3199AF742C80734A003AEBDC /* DuckPlayerOnboardingModalManager.swift in Sources */,
Expand Down Expand Up @@ -11434,6 +11460,7 @@
B6F1B02F2BCE6B47005E863C /* TunnelControllerProvider.swift in Sources */,
31A83FB62BE28D7D00F74E67 /* UserText+DBP.swift in Sources */,
3706FC31293F65D500E42796 /* PermissionButton.swift in Sources */,
317307262CD248DB00C492AB /* AutofillToolbarOnboardingPopover.swift in Sources */,
9F6434622BEC82B700D2D8A0 /* AttributionPixelHandler.swift in Sources */,
3706FC32293F65D500E42796 /* MoreOptionsMenu.swift in Sources */,
3706FC34293F65D500E42796 /* PermissionAuthorizationViewController.swift in Sources */,
Expand Down Expand Up @@ -11547,6 +11574,7 @@
9FDA6C222B79A59D00E099A9 /* BookmarkFavoriteView.swift in Sources */,
C1372EF52BBC5BAD003F8793 /* SecureTextField.swift in Sources */,
316913242BD2B6250051B46D /* DataBrokerProtectionPixelsHandler.swift in Sources */,
317307302CD2493900C492AB /* AutofillToolbarOnboardingViewModel.swift in Sources */,
843D73BC2C786E5400E4F9DC /* BookmarkListPopover.swift in Sources */,
3706FC77293F65D500E42796 /* PageObserverUserScript.swift in Sources */,
4BF0E5132AD25A2600FFEC9E /* DuckDuckGoUserAgent.swift in Sources */,
Expand Down Expand Up @@ -12496,6 +12524,7 @@
9826B0A02747DF3D0092F683 /* ContentBlocking.swift in Sources */,
4B379C2227BDBA29008A968E /* LocalAuthenticationService.swift in Sources */,
37CEFCA92A6737A2001EF741 /* CredentialsCleanupErrorHandling.swift in Sources */,
317307272CD248DB00C492AB /* AutofillToolbarOnboardingPopover.swift in Sources */,
4BB99D0326FE191E001E4761 /* SafariBookmarksReader.swift in Sources */,
316913292BD2C7570051B46D /* DataBrokerProtectionErrorViewController.swift in Sources */,
1DA6D0FD2A1FF9A100540406 /* HTTPCookie.swift in Sources */,
Expand Down Expand Up @@ -12572,6 +12601,7 @@
B6F1C80B2761C45400334924 /* LocalUnprotectedDomains.swift in Sources */,
1E25A4FE2CC937120080EFD4 /* SubscriptionCookieManageEventPixelMapping.swift in Sources */,
B69A14FA2B4D705D00B9417D /* BookmarkFolderPicker.swift in Sources */,
3173072F2CD2493900C492AB /* AutofillToolbarOnboardingViewModel.swift in Sources */,
1D36E658298AA3BA00AA485D /* InternalUserDeciderStore.swift in Sources */,
B634DBE5293C944700C3C99E /* NewWindowPolicy.swift in Sources */,
31CF3432288B0B1B0087244B /* NavigationBarBadgeAnimator.swift in Sources */,
Expand Down Expand Up @@ -12690,6 +12720,7 @@
AA5FA69D275F945C00DCE9C9 /* FaviconStore.swift in Sources */,
4B9DB0352A983B24000927DB /* WaitlistTermsAndConditionsView.swift in Sources */,
AAB8203C26B2DE0D00788AC3 /* SuggestionListCharacteristics.swift in Sources */,
3173072A2CD248EA00C492AB /* AutofillToolbarOnboardingViewController.swift in Sources */,
4B6785472AA8DE68008A5004 /* VPNUninstaller.swift in Sources */,
4B9292D42667123700AD2C21 /* BookmarkListViewController.swift in Sources */,
BD88A83E2C4F3E4300460A26 /* FeedbackCategoryProviding.swift in Sources */,
Expand Down Expand Up @@ -13203,6 +13234,7 @@
4B4D60D32A0C84F700BCD287 /* UserText+NetworkProtection.swift in Sources */,
843D73BB2C786E5400E4F9DC /* BookmarkListPopover.swift in Sources */,
B6B2400E28083B49001B8F3A /* WebViewContainerView.swift in Sources */,
3173072D2CD2490700C492AB /* AutofillToolbarOnboardingView.swift in Sources */,
AAC5E4D925D6A711007F5990 /* BookmarkStore.swift in Sources */,
B6FA893F269C424500588ECD /* PrivacyDashboardViewController.swift in Sources */,
37D2771527E870D4003365FD /* PreferencesAppearanceView.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//
// AutofillToolbarOnboardingPopover.swift
//
// Copyright © 2024 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import SwiftUI

final class AutofillToolbarOnboardingPopover: NSPopover {
let ctaCallback: (Bool) -> Void

init(ctaCallback: @escaping (Bool) -> Void) {
self.ctaCallback = ctaCallback

super.init()

self.animates = false
self.behavior = .semitransient

setupContentController()
}

required init?(coder: NSCoder) {
fatalError("\(Self.self): Bad initializer")
}

private func setupContentController() {
let controller = AutofillToolbarOnboardingViewController()
controller.ctaCallback = self.ctaCallback
contentViewController = controller
}
}
68 changes: 68 additions & 0 deletions DuckDuckGo/Autofill/Onboarding/AutofillToolbarOnboardingView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// AutofillToolbarOnboardingView.swift
//
// Copyright © 2024 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import SwiftUI
import SwiftUIExtensions

struct AutofillToolbarOnboardingView: View {
@ObservedObject var viewModel: AutofillToolbarOnboardingViewModel

enum Constants {
static let verticalSpacing: CGFloat = 16
static let panelWidth: CGFloat = 310
static let panelHeight: CGFloat = 148
}

var body: some View {
VStack(spacing: Constants.verticalSpacing) {
VStack(alignment: .leading, spacing: Constants.verticalSpacing) {
Text(UserText.autofillOnboardingPopoverTitle)
.font(.headline)
Text(UserText.autofillOnboardingPopoverMessage)
}

HStack {
createButton(title: UserText.autofillOnboardingPopoverCTAReject,
style: StandardButtonStyle(),
action: viewModel.rejectToolbarIcon)

createButton(title: UserText.autofillOnboardingPopoverCTAAccept,
style: DefaultActionButtonStyle(enabled: true),
action: viewModel.acceptToolbarIcon)
}
}
.padding()
.frame(width: Constants.panelWidth, height: Constants.panelHeight)
}

private func createButton(title: String, style: some ButtonStyle, action: @escaping () -> Void) -> some View {
Button(action: action) {
Text(title)
.font(.system(size: 13))
.fontWeight(.light)
.frame(maxWidth: .infinity)
.frame(height: 22)
}
.buttonStyle(style)
.padding(0)
}
}

#Preview {
AutofillToolbarOnboardingView(viewModel: AutofillToolbarOnboardingViewModel())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//
// AutofillToolbarOnboardingViewController.swift
//
// Copyright © 2024 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import SwiftUI

final class AutofillToolbarOnboardingViewController: NSViewController {
var ctaCallback: ((Bool) -> Void)?

private let viewModel = AutofillToolbarOnboardingViewModel()
private var hostingView: NSHostingView<AutofillToolbarOnboardingView>!

override func loadView() {
let onboardingView = AutofillToolbarOnboardingView(viewModel: viewModel)
hostingView = NSHostingView(rootView: onboardingView)
self.view = hostingView

self.setupViewModelCallbacks()
}

private func setupViewModelCallbacks() {
viewModel.ctaCallback = ctaCallback
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
//
// AutofillToolbarOnboardingViewModel.swift
//
// Copyright © 2024 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

final class AutofillToolbarOnboardingViewModel: ObservableObject {
var ctaCallback: ((Bool) -> Void)?

internal init(ctaCallback: ((Bool) -> Void)? = nil) {
self.ctaCallback = ctaCallback
}

func rejectToolbarIcon() {
ctaCallback?(false)
}

func acceptToolbarIcon() {
ctaCallback?(true)
}
}
5 changes: 5 additions & 0 deletions DuckDuckGo/Common/Localizables/UserText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,11 @@ struct UserText {
static let gpcExplanation = NSLocalizedString("gpc.explanation", value: "Tells participating websites not to sell or share your data.", comment: "GPC explanation in settings")
static let learnMore = NSLocalizedString("learnmore.link", value: "Learn More", comment: "Learn More link")

static let autofillOnboardingPopoverCTAReject = NSLocalizedString("autofill.onboarding.popover.reject", value: "No Thanks", comment: "Autofill onboarding CTA for rejection")
static let autofillOnboardingPopoverCTAAccept = NSLocalizedString("autofill.onboarding.popover.accept", value: "Add Shortcut", comment: "Autofill onboarding CTA for approval")
static let autofillOnboardingPopoverTitle = NSLocalizedString("autofill.onboarding.popover.title", value: "Add passwords shortcut?", comment: "Autofill onboarding popover title")
static let autofillOnboardingPopoverMessage = NSLocalizedString("autofill.onboarding.popover.message1", value: "You can manage your toolbar shortcuts at any time by right-clicking on the toolbar.", comment: "Autofill onboarding popover message")

static let autofillPasswordManager = NSLocalizedString("autofill.password-manager", value: "Password Manager", comment: "Autofill settings section title")
static let autofillPasswordManagerDuckDuckGo = NSLocalizedString("autofill.password-manager.duckduckgo", value: "DuckDuckGo built-in password manager", comment: "Autofill password manager row title")
static let autofillPasswordManagerBitwarden = NSLocalizedString("autofill.password-manager.bitwarden", value: "Bitwarden", comment: "Autofill password manager row title")
Expand Down
Loading

0 comments on commit 87ed70b

Please sign in to comment.