Skip to content

Commit

Permalink
DBP Waitlist business logic (#1797)
Browse files Browse the repository at this point in the history
Task/Issue URL: https://app.asana.com/0/0/1205865262436820/f

**Description**:
Waitlist flow for DBP
  • Loading branch information
Bunn authored Nov 5, 2023
1 parent 339fd68 commit fbc3cca
Show file tree
Hide file tree
Showing 26 changed files with 759 additions and 94 deletions.
22 changes: 20 additions & 2 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,11 @@
313B6A502A6EB1B900601B75 /* HTTPCookie.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DA6D0FC2A1FF9A100540406 /* HTTPCookie.swift */; };
3154FD1428E6011A00909769 /* TabShadowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3154FD1328E6011A00909769 /* TabShadowView.swift */; };
315AA07028CA5CC800200030 /* YoutubePlayerNavigationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 315AA06F28CA5CC800200030 /* YoutubePlayerNavigationHandler.swift */; };
3168506D2AF3AD1D009A2828 /* WaitlistViewControllerPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3168506C2AF3AD1C009A2828 /* WaitlistViewControllerPresenter.swift */; };
3168506E2AF3AD1D009A2828 /* WaitlistViewControllerPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3168506C2AF3AD1C009A2828 /* WaitlistViewControllerPresenter.swift */; };
3168506F2AF3AD1D009A2828 /* WaitlistViewControllerPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3168506C2AF3AD1C009A2828 /* WaitlistViewControllerPresenter.swift */; };
316850702AF3AD1D009A2828 /* WaitlistViewControllerPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3168506C2AF3AD1C009A2828 /* WaitlistViewControllerPresenter.swift */; };
316850722AF3AD58009A2828 /* DataBrokerProtectionDebugMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 316850712AF3AD58009A2828 /* DataBrokerProtectionDebugMenu.swift */; };
316C7A832A7E9BEE00AA3BAE /* BookmarksBarAppearance.swift in Sources */ = {isa = PBXBuildFile; fileRef = 850E8DFA2A6FEC5E00691187 /* BookmarksBarAppearance.swift */; };
316C7A842A7E9C1700AA3BAE /* PixelExperiment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 857E5AF42A79045800FC0FB4 /* PixelExperiment.swift */; };
316C7A852A7E9C2300AA3BAE /* BookmarksBarMenuFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85774AFE2A713D3B00DE0561 /* BookmarksBarMenuFactory.swift */; };
Expand Down Expand Up @@ -819,6 +824,7 @@
31B7C85128800A5D0049841F /* CookieConsentPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31B7C85028800A5D0049841F /* CookieConsentPopover.swift */; };
31B9226C288054D5001F55B7 /* CookieConsentPopoverManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31B9226B288054D5001F55B7 /* CookieConsentPopoverManager.swift */; };
31C3CE0228EDC1E70002C24A /* CustomRoundedCornersShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31C3CE0128EDC1E70002C24A /* CustomRoundedCornersShape.swift */; };
31C5FFB92AF64D120008A79F /* DataBrokerProtectionFeatureVisibility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31C5FFB82AF64D120008A79F /* DataBrokerProtectionFeatureVisibility.swift */; };
31C9ADE52AF0564500CEF57D /* WaitlistFeatureSetupHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31C9ADE42AF0564500CEF57D /* WaitlistFeatureSetupHandler.swift */; };
31C9ADE62AF0564500CEF57D /* WaitlistFeatureSetupHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31C9ADE42AF0564500CEF57D /* WaitlistFeatureSetupHandler.swift */; };
31C9ADE72AF0564500CEF57D /* WaitlistFeatureSetupHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 31C9ADE42AF0564500CEF57D /* WaitlistFeatureSetupHandler.swift */; };
Expand Down Expand Up @@ -3882,6 +3888,8 @@
313AEDA0287CAD1D00E1E8F4 /* CookieConsentUserPermissionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CookieConsentUserPermissionView.swift; sourceTree = "<group>"; };
3154FD1328E6011A00909769 /* TabShadowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabShadowView.swift; sourceTree = "<group>"; };
315AA06F28CA5CC800200030 /* YoutubePlayerNavigationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YoutubePlayerNavigationHandler.swift; sourceTree = "<group>"; };
3168506C2AF3AD1C009A2828 /* WaitlistViewControllerPresenter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WaitlistViewControllerPresenter.swift; sourceTree = "<group>"; };
316850712AF3AD58009A2828 /* DataBrokerProtectionDebugMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataBrokerProtectionDebugMenu.swift; sourceTree = "<group>"; };
3171D6B72889849F0068632A /* CookieManagedNotificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CookieManagedNotificationView.swift; sourceTree = "<group>"; };
3171D6B9288984D00068632A /* BadgeAnimationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgeAnimationView.swift; sourceTree = "<group>"; };
3171D6DA2889B64D0068632A /* CookieManagedNotificationContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CookieManagedNotificationContainerView.swift; sourceTree = "<group>"; };
Expand All @@ -3900,6 +3908,7 @@
31B7C85028800A5D0049841F /* CookieConsentPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CookieConsentPopover.swift; sourceTree = "<group>"; };
31B9226B288054D5001F55B7 /* CookieConsentPopoverManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CookieConsentPopoverManager.swift; sourceTree = "<group>"; };
31C3CE0128EDC1E70002C24A /* CustomRoundedCornersShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomRoundedCornersShape.swift; sourceTree = "<group>"; };
31C5FFB82AF64D120008A79F /* DataBrokerProtectionFeatureVisibility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataBrokerProtectionFeatureVisibility.swift; sourceTree = "<group>"; };
31C9ADE42AF0564500CEF57D /* WaitlistFeatureSetupHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WaitlistFeatureSetupHandler.swift; sourceTree = "<group>"; };
31CF3431288B0B1B0087244B /* NavigationBarBadgeAnimator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarBadgeAnimator.swift; sourceTree = "<group>"; };
31D5375B291D944100407A95 /* PasswordManagementBitwardenItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordManagementBitwardenItemView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -5371,10 +5380,12 @@
3192EC862A4DCF0E001E97A5 /* DBP */ = {
isa = PBXGroup;
children = (
316850712AF3AD58009A2828 /* DataBrokerProtectionDebugMenu.swift */,
3192EC872A4DCF21001E97A5 /* DBPHomeViewController.swift */,
3139A1512AA4B3C000969C7D /* DataBrokerProtectionManager.swift */,
7B6D98662ADEB4B000CD35FE /* DataBrokerProtectionLoginItemScheduler.swift */,
9D8FA00B2AC5BDCE005DD0D0 /* LoginItem+DataBrokerProtection.swift */,
31C5FFB82AF64D120008A79F /* DataBrokerProtectionFeatureVisibility.swift */,
);
path = DBP;
sourceTree = "<group>";
Expand Down Expand Up @@ -6171,6 +6182,8 @@
children = (
7BD8679A2A9E9E000063B9F7 /* NetworkProtectionFeatureVisibility.swift */,
4B6785432AA8DE1F008A5004 /* NetworkProtectionFeatureDisabler.swift */,
31F2D1FE2AF026D800BF0144 /* WaitlistTermsAndConditionsActionHandler.swift */,
31C9ADE42AF0564500CEF57D /* WaitlistFeatureSetupHandler.swift */,
4B9DB0072A983B23000927DB /* Waitlist.swift */,
4B9DB0082A983B23000927DB /* Networking */,
4B9DB00B2A983B24000927DB /* Models */,
Expand All @@ -6194,8 +6207,6 @@
isa = PBXGroup;
children = (
4B9DB00C2A983B24000927DB /* WaitlistViewModel.swift */,
31F2D1FE2AF026D800BF0144 /* WaitlistTermsAndConditionsActionHandler.swift */,
31C9ADE42AF0564500CEF57D /* WaitlistFeatureSetupHandler.swift */,
);
path = Models;
sourceTree = "<group>";
Expand All @@ -6216,6 +6227,7 @@
4B9DB0122A983B24000927DB /* WaitlistSteps */,
4B9DB0182A983B24000927DB /* WaitlistDialogView.swift */,
4B9DB0192A983B24000927DB /* WaitlistModalViewController.swift */,
3168506C2AF3AD1C009A2828 /* WaitlistViewControllerPresenter.swift */,
4B9DB01A2A983B24000927DB /* WaitlistRootView.swift */,
);
path = Views;
Expand Down Expand Up @@ -10077,6 +10089,7 @@
31929FDB2A4C4CFF0084EA89 /* PixelParameters.swift in Sources */,
31929FDD2A4C4CFF0084EA89 /* FaviconImageCache.swift in Sources */,
31929FDE2A4C4CFF0084EA89 /* TabBarViewController.swift in Sources */,
316850722AF3AD58009A2828 /* DataBrokerProtectionDebugMenu.swift in Sources */,
31929FDF2A4C4CFF0084EA89 /* BookmarkOutlineViewDataSource.swift in Sources */,
31929FE02A4C4CFF0084EA89 /* DataImportStatusProviding.swift in Sources */,
31929FE12A4C4CFF0084EA89 /* PasswordManagementBitwardenItemView.swift in Sources */,
Expand Down Expand Up @@ -10332,6 +10345,7 @@
3192A0CC2A4C4CFF0084EA89 /* PasswordManagementListSection.swift in Sources */,
3192A0CD2A4C4CFF0084EA89 /* FaviconReferenceCache.swift in Sources */,
31F2D2012AF026D800BF0144 /* WaitlistTermsAndConditionsActionHandler.swift in Sources */,
3168506F2AF3AD1D009A2828 /* WaitlistViewControllerPresenter.swift in Sources */,
4B9DB0372A983B24000927DB /* WaitlistTermsAndConditionsView.swift in Sources */,
3192A0CE2A4C4CFF0084EA89 /* BookmarkTreeController.swift in Sources */,
3192A0CF2A4C4CFF0084EA89 /* FirefoxEncryptionKeyReader.swift in Sources */,
Expand Down Expand Up @@ -10552,6 +10566,7 @@
3192A1932A4C4CFF0084EA89 /* PinnedTabsViewModel.swift in Sources */,
3192A1942A4C4CFF0084EA89 /* BookmarkList.swift in Sources */,
3192A1952A4C4CFF0084EA89 /* NEOnDemandRuleExtension.swift in Sources */,
31C5FFB92AF64D120008A79F /* DataBrokerProtectionFeatureVisibility.swift in Sources */,
3192A1962A4C4CFF0084EA89 /* BookmarkTableRowView.swift in Sources */,
3192A1972A4C4CFF0084EA89 /* FavoritesView.swift in Sources */,
3192A1982A4C4CFF0084EA89 /* HomePage.swift in Sources */,
Expand Down Expand Up @@ -10913,6 +10928,7 @@
3706FB46293F65D500E42796 /* FirePopoverWrapperViewController.swift in Sources */,
3706FB47293F65D500E42796 /* NSPasteboardItemExtension.swift in Sources */,
3706FB48293F65D500E42796 /* AutofillPreferencesModel.swift in Sources */,
3168506E2AF3AD1D009A2828 /* WaitlistViewControllerPresenter.swift in Sources */,
3706FB49293F65D500E42796 /* NSException+Catch.swift in Sources */,
3706FB4A293F65D500E42796 /* PasswordManagementNoteModel.swift in Sources */,
3706FB4B293F65D500E42796 /* CookieNotificationAnimationModel.swift in Sources */,
Expand Down Expand Up @@ -12355,6 +12371,7 @@
4B957B892AC7AE700062CA31 /* ToggleableScrollView.swift in Sources */,
4B957B8A2AC7AE700062CA31 /* TabCleanupPreparer.swift in Sources */,
4B957B8B2AC7AE700062CA31 /* NetworkProtectionOptionKeyExtension.swift in Sources */,
316850702AF3AD1D009A2828 /* WaitlistViewControllerPresenter.swift in Sources */,
1E2AE4CB2ACB21C800684E0A /* HardwareModel.swift in Sources */,
4B957B8C2AC7AE700062CA31 /* UserScripts.swift in Sources */,
4B957B8D2AC7AE700062CA31 /* NSWorkspaceExtension.swift in Sources */,
Expand Down Expand Up @@ -12840,6 +12857,7 @@
B687B7CC2947A1E9001DEA6F /* ExternalAppSchemeHandler.swift in Sources */,
B65536AE2685E17200085A79 /* GeolocationService.swift in Sources */,
4B02198925E05FAC00ED7DEA /* FireproofingURLExtensions.swift in Sources */,
3168506D2AF3AD1D009A2828 /* WaitlistViewControllerPresenter.swift in Sources */,
7B1E819E27C8874900FF0E60 /* ContentOverlayPopover.swift in Sources */,
3154FD1428E6011A00909769 /* TabShadowView.swift in Sources */,
1D43EB3C292B664A0065E5D6 /* BWMessageIdGenerator.swift in Sources */,
Expand Down
28 changes: 26 additions & 2 deletions DuckDuckGo/AppDelegate/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,12 @@ final class AppDelegate: NSObject, NSApplicationDelegate, FileDownloadManagerDel
NetworkProtectionAppEvents().applicationDidFinishLaunching()
UNUserNotificationCenter.current().delegate = self
#endif

#if DBP
Task {
try? await DataBrokerProtectionWaitlist().redeemDataBrokerProtectionInviteCodeIfAvailable()
}
#endif
}

func applicationDidBecomeActive(_ notification: Notification) {
Expand All @@ -233,6 +239,12 @@ final class AppDelegate: NSObject, NSApplicationDelegate, FileDownloadManagerDel

NetworkProtectionAppEvents().applicationDidBecomeActive()
#endif

#if DBP
Task {
try? await DataBrokerProtectionWaitlist().redeemDataBrokerProtectionInviteCodeIfAvailable()
}
#endif
}

func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
Expand Down Expand Up @@ -373,7 +385,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate, FileDownloadManagerDel

}

#if NETWORK_PROTECTION
#if NETWORK_PROTECTION || DBP

extension AppDelegate: UNUserNotificationCenterDelegate {

Expand All @@ -387,12 +399,24 @@ extension AppDelegate: UNUserNotificationCenterDelegate {
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {
if response.actionIdentifier == UNNotificationDefaultActionIdentifier {

#if NETWORK_PROTECTION
if response.notification.request.identifier == NetworkProtectionWaitlist.notificationIdentifier {
if NetworkProtectionWaitlist().readyToAcceptTermsAndConditions {
DailyPixel.fire(pixel: .networkProtectionWaitlistNotificationTapped, frequency: .dailyAndCount, includeAppVersionParameter: true)
WaitlistModalViewController.show()
NetworkProtectionWaitlistViewControllerPresenter.show()
}
}
#endif

#if DBP
if response.notification.request.identifier == DataBrokerProtectionWaitlist.notificationIdentifier {
if DataBrokerProtectionWaitlist().readyToAcceptTermsAndConditions {
DailyPixel.fire(pixel: .dataBrokerProtectionWaitlistNotificationTapped, frequency: .dailyAndCount, includeAppVersionParameter: true)
DataBrokerProtectionWaitlistViewControllerPresenter.show()
}
}
#endif
}

completionHandler()
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo/AppDelegate/URLEventHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ final class URLEventHandler {
if url.scheme == "networkprotection" {
handleNetworkProtectionURL(url)
} else {
WaitlistModalViewController.dismissWaitlistModalViewControllerIfNecessary(url)
WaitlistModalDismisser.dismissWaitlistModalViewControllerIfNecessary(url)
WindowControllersManager.shared.show(url: url, newTab: true)
}
#else
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "DBP-Icon.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.
4 changes: 4 additions & 0 deletions DuckDuckGo/Common/Utilities/UserDefaultsWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ public struct UserDefaultsWrapper<T> {

case firstLaunchDate = "first.app.launch.date"

// Data Broker Protection

case dataBrokerProtectionTermsAndConditionsAccepted = "data-broker-protection.waitlist-terms-and-conditions.accepted"

// Network Protection

case networkProtectionExcludedRoutes = "netp.excluded-routes"
Expand Down
117 changes: 117 additions & 0 deletions DuckDuckGo/DBP/DataBrokerProtectionDebugMenu.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
//
// DataBrokerProtectionDebugMenu.swift
//
// Copyright © 2023 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.
//

#if DBP

import DataBrokerProtection
import Foundation
import AppKit
import Common

@MainActor
final class DataBrokerProtectionDebugMenu: NSMenu {

private let waitlistTokenItem = NSMenuItem(title: "Waitlist Token:")
private let waitlistTimestampItem = NSMenuItem(title: "Waitlist Timestamp:")
private let waitlistInviteCodeItem = NSMenuItem(title: "Waitlist Invite Code:")
private let waitlistTermsAndConditionsAcceptedItem = NSMenuItem(title: "T&C Accepted:")

init() {
super.init(title: "Personal Information Removal")

buildItems {
NSMenuItem(title: "Waitlist") {
NSMenuItem(title: "Reset Waitlist State", action: #selector(DataBrokerProtectionDebugMenu.resetWaitlistState))
.targetting(self)
NSMenuItem(title: "Reset T&C Acceptance", action: #selector(DataBrokerProtectionDebugMenu.resetTermsAndConditionsAcceptance))
.targetting(self)

NSMenuItem(title: "Send Notification", action: #selector(DataBrokerProtectionDebugMenu.sendWaitlistAvailableNotification))
.targetting(self)

NSMenuItem(title: "Fetch Invite Code", action: #selector(DataBrokerProtectionDebugMenu.fetchInviteCode))
.targetting(self)
NSMenuItem.separator()

waitlistTokenItem
waitlistTimestampItem
waitlistInviteCodeItem
waitlistTermsAndConditionsAcceptedItem
}
}
}

required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

// MARK: - Menu State Update

override func update() {
updateWaitlistItems()
}

// MARK: - Menu functions

@objc private func resetWaitlistState() {
DataBrokerProtectionWaitlist().waitlistStorage.deleteWaitlistState()
UserDefaultsAuthenticationData().reset()
UserDefaults().removeObject(forKey: UserDefaultsWrapper<Bool>.Key.dataBrokerProtectionTermsAndConditionsAccepted.rawValue)
NotificationCenter.default.post(name: .dataBrokerProtectionWaitlistAccessChanged, object: nil)
os_log("DBP waitlist state cleaned", log: .dataBrokerProtection)
}

@objc private func resetTermsAndConditionsAcceptance() {
UserDefaults().removeObject(forKey: UserDefaultsWrapper<Bool>.Key.dataBrokerProtectionTermsAndConditionsAccepted.rawValue)
NotificationCenter.default.post(name: .dataBrokerProtectionWaitlistAccessChanged, object: nil)
os_log("DBP waitlist terms and conditions cleaned", log: .dataBrokerProtection)
}

@objc private func sendWaitlistAvailableNotification() {
DataBrokerProtectionWaitlist().sendInviteCodeAvailableNotification(completion: nil)

os_log("DBP waitlist notification sent", log: .dataBrokerProtection)
}

@objc private func fetchInviteCode() {
os_log("Fetching invite code...", log: .dataBrokerProtection)

Task {
try? await DataBrokerProtectionWaitlist().redeemDataBrokerProtectionInviteCodeIfAvailable()
}
}

// MARK: - Utility Functions

private func updateWaitlistItems() {
let waitlistStorage = WaitlistKeychainStore(waitlistIdentifier: DataBrokerProtectionWaitlist.identifier)
waitlistTokenItem.title = "Waitlist Token: \(waitlistStorage.getWaitlistToken() ?? "N/A")"
waitlistInviteCodeItem.title = "Waitlist Invite Code: \(waitlistStorage.getWaitlistInviteCode() ?? "N/A")"

if let timestamp = waitlistStorage.getWaitlistTimestamp() {
waitlistTimestampItem.title = "Waitlist Timestamp: \(String(describing: timestamp))"
} else {
waitlistTimestampItem.title = "Waitlist Timestamp: N/A"
}

let accepted = UserDefaults().bool(forKey: UserDefaultsWrapper<Bool>.Key.dataBrokerProtectionTermsAndConditionsAccepted.rawValue)
waitlistTermsAndConditionsAcceptedItem.title = "T&C Accepted: \(accepted ? "Yes" : "No")"
}
}

#endif
Loading

0 comments on commit fbc3cca

Please sign in to comment.