From b6cb502deae04efa8560dfa67eaad5aef2016059 Mon Sep 17 00:00:00 2001 From: Sabrina Tardio <44158575+SabrinaTardio@users.noreply.github.com> Date: Thu, 14 Dec 2023 12:25:48 +0100 Subject: [PATCH 1/9] change order of items in autofill add new item (#1967) Task/Issue URL: https://app.asana.com/0/1177771139624306/1206145724083864/f **Description**: Change order of autofill item in Passwords manager when adding new items --- .../SecureVault/View/PasswordManagementViewController.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift b/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift index 9b26bbae9e..7360356841 100644 --- a/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift +++ b/DuckDuckGo/SecureVault/View/PasswordManagementViewController.swift @@ -777,9 +777,9 @@ final class PasswordManagementViewController: NSViewController { } menu.items = [ - createMenuItem(title: UserText.pmNewCard, action: #selector(createNewCreditCard), imageName: "CreditCardGlyph"), createMenuItem(title: UserText.pmNewLogin, action: #selector(createNewLogin), imageName: "LoginGlyph"), - createMenuItem(title: UserText.pmNewIdentity, action: #selector(createNewIdentity), imageName: "IdentityGlyph") + createMenuItem(title: UserText.pmNewIdentity, action: #selector(createNewIdentity), imageName: "IdentityGlyph"), + createMenuItem(title: UserText.pmNewCard, action: #selector(createNewCreditCard), imageName: "CreditCardGlyph"), ] return menu From 076cd44819a569b076dddf32280950e69a1e3585 Mon Sep 17 00:00:00 2001 From: Sabrina Tardio <44158575+SabrinaTardio@users.noreply.github.com> Date: Thu, 14 Dec 2023 14:50:16 +0100 Subject: [PATCH 2/9] remove QR code from save recovery PDF view (#1968) Task/Issue URL: https://app.asana.com/0/0/1206162433383042/f **Description**: Remove QR code from save recovery PDF view --- .../Views/Dialogs/SaveRecoveryPDFView.swift | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/Dialogs/SaveRecoveryPDFView.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/Dialogs/SaveRecoveryPDFView.swift index e4b965bd0c..61e93944d3 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/Dialogs/SaveRecoveryPDFView.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/Dialogs/SaveRecoveryPDFView.swift @@ -31,17 +31,14 @@ struct SaveRecoveryPDFView: View { SyncUIViews.TextDetailMultiline(text: UserText.recoveryPDFExplanation) } VStack(alignment: .leading, spacing: 20) { - HStack { - QRCode(string: code, size: CGSize(width: 56, height: 56)) - Text(code) - .kerning(2) - .multilineTextAlignment(.leading) - .lineSpacing(5) - .lineLimit(3) - .font(Font.custom("SF Mono", size: 12)) - .fixedSize(horizontal: false, vertical: true) - } - .frame(width: 340) + Text(code) + .kerning(2) + .multilineTextAlignment(.leading) + .lineSpacing(5) + .lineLimit(3) + .font(Font.custom("SF Mono", size: 12)) + .fixedSize(horizontal: false, vertical: true) + .frame(width: 340) HStack { Button { viewModel.delegate?.copyCode() From 9dc6d1271972a7bdd61cd1307012ce47fbb0c1ca Mon Sep 17 00:00:00 2001 From: Sabrina Tardio <44158575+SabrinaTardio@users.noreply.github.com> Date: Thu, 14 Dec 2023 15:08:24 +0100 Subject: [PATCH 3/9] Improve sync set up error handling (#1966) Task/Issue URL: https://app.asana.com/0/1204186595873227/1206162433383039/f **Description**: Improve Error handling for sync set up flow --- .../Preferences/Model/SyncPreferences.swift | 36 +++++++----- .../ViewModels/ManagementDialogModel.swift | 4 +- .../ViewModels/ManagementViewModel.swift | 48 ++++++++++++++- .../Views/Dialogs/DeviceDetailsView.swift | 58 ++++++++++--------- .../SyncUI/Views/ManagementDialog.swift | 21 ++++++- .../Sources/SyncUI/Views/ManagementView.swift | 3 - .../Sources/SyncUI/internal/UserText.swift | 9 +++ 7 files changed, 130 insertions(+), 49 deletions(-) diff --git a/DuckDuckGo/Preferences/Model/SyncPreferences.swift b/DuckDuckGo/Preferences/Model/SyncPreferences.swift index e2cd020d69..7c15bd0bab 100644 --- a/DuckDuckGo/Preferences/Model/SyncPreferences.swift +++ b/DuckDuckGo/Preferences/Model/SyncPreferences.swift @@ -57,7 +57,7 @@ final class SyncPreferences: ObservableObject, SyncUI.ManagementViewModel { } @Published var shouldShowErrorMessage: Bool = false - @Published private(set) var errorMessage: String? + @Published private(set) var syncErrorMessage: SyncErrorMessage? @Published var isCreatingAccount: Bool = false @@ -120,7 +120,7 @@ final class SyncPreferences: ObservableObject, SyncUI.ManagementViewModel { } .store(in: &cancellables) - $errorMessage + $syncErrorMessage .map { $0 != nil } .receive(on: DispatchQueue.main) .assign(to: \.shouldShowErrorMessage, onWeaklyHeld: self) @@ -179,7 +179,8 @@ final class SyncPreferences: ObservableObject, SyncUI.ManagementViewModel { UserDefaults.standard.set(false, forKey: UserDefaultsWrapper.Key.syncBookmarksPaused.rawValue) UserDefaults.standard.set(false, forKey: UserDefaultsWrapper.Key.syncCredentialsPaused.rawValue) } catch { - errorMessage = String(describing: error) + managementDialogModel.syncErrorMessage + = SyncErrorMessage(type: .unableToDeleteData, description: error.localizedDescription) } } } @@ -328,7 +329,8 @@ extension SyncPreferences: ManagementDialogModelDelegate { UserDefaults.standard.set(false, forKey: UserDefaultsWrapper.Key.syncBookmarksPaused.rawValue) UserDefaults.standard.set(false, forKey: UserDefaultsWrapper.Key.syncCredentialsPaused.rawValue) } catch { - managementDialogModel.errorMessage = String(describing: error) + managementDialogModel.syncErrorMessage + = SyncErrorMessage(type: .unableToDeleteData, description: error.localizedDescription) } } } @@ -338,9 +340,11 @@ extension SyncPreferences: ManagementDialogModelDelegate { do { self.devices = [] let devices = try await syncService.updateDeviceName(name) + managementDialogModel.endFlow() mapDevices(devices) } catch { - managementDialogModel.errorMessage = String(describing: error) + managementDialogModel.syncErrorMessage + = SyncErrorMessage(type: .unableToUpdateDeviceName, description: error.localizedDescription) } } } @@ -380,7 +384,8 @@ extension SyncPreferences: ManagementDialogModelDelegate { Pixel.fire(.syncSignupDirect) presentDialog(for: .saveRecoveryCode(recoveryCode ?? "")) } catch { - managementDialogModel.errorMessage = String(describing: error) + managementDialogModel.syncErrorMessage + = SyncErrorMessage(type: .unableToSync, description: error.localizedDescription) } } } @@ -405,7 +410,8 @@ extension SyncPreferences: ManagementDialogModelDelegate { } } catch { if syncService.account == nil { - managementDialogModel.errorMessage = String(describing: error) + managementDialogModel.syncErrorMessage + = SyncErrorMessage(type: .unableToSync, description: error.localizedDescription) } } } @@ -420,7 +426,8 @@ extension SyncPreferences: ManagementDialogModelDelegate { Task { @MainActor in do { guard let syncCode = try? SyncCode.decodeBase64String(recoveryCode) else { - managementDialogModel.errorMessage = "Invalid code" + managementDialogModel.syncErrorMessage + = SyncErrorMessage(type: .invalidCode, description: "") return } presentDialog(for: .prepareToSync) @@ -447,11 +454,13 @@ extension SyncPreferences: ManagementDialogModelDelegate { // The UI will update when the devices list changes. } else { - managementDialogModel.errorMessage = "Invalid code" + managementDialogModel.syncErrorMessage + = SyncErrorMessage(type: .invalidCode, description: "") return } } catch { - managementDialogModel.errorMessage = String(describing: error) + managementDialogModel.syncErrorMessage + = SyncErrorMessage(type: .unableToSync, description: error.localizedDescription) } } } @@ -481,7 +490,8 @@ extension SyncPreferences: ManagementDialogModelDelegate { do { try data.writeFileWithProgress(to: location) } catch { - managementDialogModel.errorMessage = String(describing: error) + managementDialogModel.syncErrorMessage + = SyncErrorMessage(type: .unableCreateRecoveryPDF, description: error.localizedDescription) } } @@ -495,8 +505,8 @@ extension SyncPreferences: ManagementDialogModelDelegate { refreshDevices() managementDialogModel.endFlow() } catch { - managementDialogModel.errorMessage = String(describing: error) - } + managementDialogModel.syncErrorMessage + = SyncErrorMessage(type: .unableToRemoveDevice, description: error.localizedDescription) } } } diff --git a/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/ManagementDialogModel.swift b/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/ManagementDialogModel.swift index 2265e212e0..eaf0aa2808 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/ManagementDialogModel.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/ManagementDialogModel.swift @@ -40,12 +40,12 @@ public final class ManagementDialogModel: ObservableObject { public var codeToDisplay: String? @Published public var shouldShowErrorMessage: Bool = false - @Published public var errorMessage: String? + @Published public var syncErrorMessage: SyncErrorMessage? public weak var delegate: ManagementDialogModelDelegate? public init() { - shouldShowErrorMessageCancellable = $errorMessage + shouldShowErrorMessageCancellable = $syncErrorMessage .map { $0 != nil } .receive(on: DispatchQueue.main) .sink { [weak self] hasError in diff --git a/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/ManagementViewModel.swift b/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/ManagementViewModel.swift index a1218f2153..89cb583ab2 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/ManagementViewModel.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/ViewModels/ManagementViewModel.swift @@ -23,7 +23,7 @@ public protocol ManagementViewModel: ObservableObject { var isSyncEnabled: Bool { get } var isCreatingAccount: Bool { get } var shouldShowErrorMessage: Bool { get set } - var errorMessage: String? { get } + var syncErrorMessage: SyncErrorMessage? { get } var isSyncBookmarksPaused: Bool { get } var isSyncCredentialsPaused: Bool { get } @@ -48,3 +48,49 @@ public protocol ManagementViewModel: ObservableObject { func recoverDataPressed() func turnOffSyncPressed() } + +public enum SyncErrorType { + case unableToSync + case unableToGetDevices + case unableToUpdateDeviceName + case unableToTurnSyncOff + case unableToDeleteData + case unableToRemoveDevice + case invalidCode + case unableCreateRecoveryPDF + + var title: String { + return UserText.syncErrorAlertTitle + } + + var description: String { + switch self { + case .unableToSync: + return UserText.unableToSyncDescription + case .unableToGetDevices: + return UserText.unableToGetDevicesDescription + case .unableToUpdateDeviceName: + return UserText.unableToUpdateDeviceNameDescription + case .unableToTurnSyncOff: + return UserText.unableToTurnSyncOffDescription + case .unableToDeleteData: + return UserText.unableToDeleteDataDescription + case .unableToRemoveDevice: + return UserText.unableToRemoveDeviceDescription + case .invalidCode: + return UserText.invalidCodeDescription + case .unableCreateRecoveryPDF: + return UserText.unableCreateRecoveryPdfDescription + } + } +} + +public struct SyncErrorMessage { + var type: SyncErrorType + var errorDescription: String + + public init(type: SyncErrorType, description: String) { + self.type = type + self.errorDescription = description + } +} diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/Dialogs/DeviceDetailsView.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/Dialogs/DeviceDetailsView.swift index 017021da64..dc3b53f8b1 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/Dialogs/DeviceDetailsView.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/Dialogs/DeviceDetailsView.swift @@ -26,6 +26,7 @@ struct DeviceDetailsView: View { let device: SyncDevice @State var deviceName = "" + @State private var isLoading = false var canSave: Bool { !deviceName.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty && @@ -35,39 +36,42 @@ struct DeviceDetailsView: View { func submit() { guard canSave else { return } model.delegate?.updateDeviceName(deviceName) - model.endFlow() } var body: some View { - SyncDialog { - VStack(spacing: 20) { - SyncUIViews.TextHeader(text: UserText.deviceDetailsTitle) - - HStack { - Text(UserText.deviceDetailsLabel) - .font(.system(size: 13, weight: .semibold)) - TextField(UserText.deviceDetailsPrompt, text: $deviceName, onCommit: submit) - .textFieldStyle(RoundedBorderTextFieldStyle()) + if isLoading { + ProgressView() + .padding() + } else { + SyncDialog { + VStack(spacing: 20) { + SyncUIViews.TextHeader(text: UserText.deviceDetailsTitle) + HStack { + Text(UserText.deviceDetailsLabel) + .font(.system(size: 13, weight: .semibold)) + TextField(UserText.deviceDetailsPrompt, text: $deviceName, onCommit: submit) + .textFieldStyle(RoundedBorderTextFieldStyle()) + } + .padding(.horizontal, 10) + .padding(.vertical, 14.5) + .roundedBorder() } - .padding(.horizontal, 10) - .padding(.vertical, 14.5) - .roundedBorder() - } - } buttons: { - Button(UserText.cancel) { - model.endFlow() + } buttons: { + Button(UserText.cancel) { + model.endFlow() + } + .buttonStyle(DismissActionButtonStyle()) + Button(UserText.ok) { + submit() + isLoading = true + } + .disabled(!canSave) + .buttonStyle(DefaultActionButtonStyle(enabled: canSave)) } - .buttonStyle(DismissActionButtonStyle()) - Button(UserText.ok) { - submit() + .frame(width: 360, height: 178) + .onAppear { + deviceName = device.name } - .disabled(!canSave) - .buttonStyle(DefaultActionButtonStyle(enabled: canSave)) - - } - .frame(width: 360, height: 178) - .onAppear { - deviceName = device.name } } } diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/ManagementDialog.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/ManagementDialog.swift index 34e25dc3af..6af2621ee1 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/ManagementDialog.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/ManagementDialog.swift @@ -36,6 +36,19 @@ public struct ManagementDialog: View { @ObservedObject public var model: ManagementDialogModel @ObservedObject public var recoveryCodeModel: RecoveryCodeViewModel + var errorTitle: String { + return model.syncErrorMessage?.type.title ?? "Sync Error" + } + + var errorDescription: String { + guard let typeDescription = model.syncErrorMessage?.type.description, + let errorDescription = model.syncErrorMessage?.errorDescription + else { + return "" + } + return typeDescription + "\n" + errorDescription + } + public init(model: ManagementDialogModel, recoveryCodeModel: RecoveryCodeViewModel = .init()) { self.model = model self.recoveryCodeModel = recoveryCodeModel @@ -45,9 +58,11 @@ public struct ManagementDialog: View { content .alert(isPresented: $model.shouldShowErrorMessage) { Alert( - title: Text("Unable to turn on Sync"), - message: Text(model.errorMessage ?? "An error occurred"), - dismissButton: .default(Text(UserText.ok)) + title: Text(errorTitle), + message: Text(errorDescription), + dismissButton: .default(Text(UserText.ok)) { + model.endFlow() + } ) } } diff --git a/LocalPackages/SyncUI/Sources/SyncUI/Views/ManagementView.swift b/LocalPackages/SyncUI/Sources/SyncUI/Views/ManagementView.swift index feb18ccd3a..11072dd02f 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/Views/ManagementView.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/Views/ManagementView.swift @@ -51,8 +51,5 @@ public struct ManagementView: View where ViewModel: ManagementViewMod } } } - .alert(isPresented: $model.shouldShowErrorMessage) { - Alert(title: Text("Unable to turn on Sync"), message: Text(model.errorMessage ?? "An error occurred"), dismissButton: .default(Text(UserText.ok))) - } } } diff --git a/LocalPackages/SyncUI/Sources/SyncUI/internal/UserText.swift b/LocalPackages/SyncUI/Sources/SyncUI/internal/UserText.swift index 38c7c0aa46..fe4f6c3fef 100644 --- a/LocalPackages/SyncUI/Sources/SyncUI/internal/UserText.swift +++ b/LocalPackages/SyncUI/Sources/SyncUI/internal/UserText.swift @@ -144,6 +144,15 @@ enum UserText { static let credentialsLimitExceededDescription = NSLocalizedString("prefrences.sync.credentials-limit-exceeded-description", value: "Logins limit exceeded. Delete some to resume syncing.", comment: "Description for sync credentials limits exceeded warning") static let bookmarksLimitExceededAction = NSLocalizedString("prefrences.sync.bookmarks-limit-exceeded-action", value: "Manage Bookmarks", comment: "Button title for sync bookmarks limits exceeded warning to manage bookmarks") static let credentialsLimitExceededAction = NSLocalizedString("prefrences.sync.credentials-limit-exceeded-action", value: "Manage Logins", comment: "Button title for sync credentials limits exceeded warning to manage logins") + static let syncErrorAlertTitle = NSLocalizedString("alert.sync-error", value: "Sync Error", comment: "Title for sync error alert") + static let unableToSyncDescription = NSLocalizedString("alert.unable-to-sync-description", value: "Unable to sync.", comment: "Description for unable to sync error") + static let unableToGetDevicesDescription = NSLocalizedString("alert.unable-to-get-devices-description", value: "Unable to retrieve the list of connected devices.", comment: "Description for unable to get devices error") + static let unableToUpdateDeviceNameDescription = NSLocalizedString("alert.unable-to-update-device-name-description", value: "Unable to update the name of the device.", comment: "Description for unable to update device name error") + static let unableToTurnSyncOffDescription = NSLocalizedString("alert.unable-to-turn-sync-off-description", value: "Unable to turn sync off.", comment: "Description for unable to turn sync off error") + static let unableToDeleteDataDescription = NSLocalizedString("alert.unable-to-delete-data-description", value: "Unable to delete data on the server.", comment: "Description for unable to delete data error") + static let unableToRemoveDeviceDescription = NSLocalizedString("alert.unable-to-remove-device-description", value: "Unable to remove the specified device from the synchronized devices.", comment: "Description for unable to remove device error") + static let invalidCodeDescription = NSLocalizedString("alert.invalid-code-description", value: "The code used is invalid.", comment: "Description for invalid code error") + static let unableCreateRecoveryPdfDescription = NSLocalizedString("alert.unable-to-create-recovery-pdf-description", value: "There was a problem creating the recovery PDF.", comment: "Description for unable to create recovery pdf error") static let fetchFaviconsOnboardingTitle = NSLocalizedString("prefrences.sync.fetch-favicons-onboarding-title", value: "Download Missing Icons?", comment: "Title for fetch favicons onboarding dialog") static let fetchFaviconsOnboardingMessage = NSLocalizedString("prefrences.sync.fetch-favicons-onboarding-message", value: "Do you want this device to automatically download icons for any new bookmarks synced from your other devices? This will expose the download to your network any time a bookmark is synced.", comment: "Text for fetch favicons onboarding dialog") From 593217c3ff46a9d0015b94c6dc9dd1c5858836a2 Mon Sep 17 00:00:00 2001 From: Anh Do <18567+quanganhdo@users.noreply.github.com> Date: Thu, 14 Dec 2023 15:14:43 -0500 Subject: [PATCH 4/9] Fix date generator for time machine (#1969) Fixes a flaky test due to timezone setting. --- .../Tests/PixelKitTests/PixelKitTests.swift | 92 +++++++++---------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/LocalPackages/PixelKit/Tests/PixelKitTests/PixelKitTests.swift b/LocalPackages/PixelKit/Tests/PixelKitTests/PixelKitTests.swift index 99504d64c1..8450c0df9c 100644 --- a/LocalPackages/PixelKit/Tests/PixelKitTests/PixelKitTests.swift +++ b/LocalPackages/PixelKit/Tests/PixelKitTests/PixelKitTests.swift @@ -228,51 +228,51 @@ final class PixelKitTests: XCTestCase { wait(for: [fireCallbackCalled], timeout: 0.5) } -// /// Test firing a daily pixel a few times -// func testDailyPixelFrequency() { -// // Prepare test parameters -// let appVersion = "1.0.5" -// let headers = ["a": "2", "b": "3", "c": "2000"] -// let log = OSLog(subsystem: "TestSubsystem", category: "TestCategory") -// let event = TestEvent.dailyEvent -// let userDefaults = userDefaults() -// -// let timeMachine = TimeMachine() -// -// // Set expectations -// let fireCallbackCalled = expectation(description: "Expect the pixel firing callback to be called") -// fireCallbackCalled.expectedFulfillmentCount = 3 -// fireCallbackCalled.assertForOverFulfill = true -// -// // Prepare mock to validate expectations -// let pixelKit = PixelKit(dryRun: false, -// appVersion: appVersion, -// defaultHeaders: headers, -// log: log, -// dailyPixelCalendar: nil, -// dateGenerator: timeMachine.now, -// defaults: userDefaults) { _, _, _, _, _, _ in -// fireCallbackCalled.fulfill() -// } -// -// // Run test -// pixelKit.fire(event, frequency: .dailyOnly) // Fired -// -// timeMachine.travel(by: 60 * 60 * 2) -// pixelKit.fire(event, frequency: .dailyOnly) // Skipped (2 hours since last fire) -// -// timeMachine.travel(by: 60 * 60 * 24 + 1000) -// pixelKit.fire(event, frequency: .dailyOnly) // Fired (24 hours + 1000 seconds since last fire) -// -// timeMachine.travel(by: 60 * 60 * 10) -// pixelKit.fire(event, frequency: .dailyOnly) // Skipped (10 hours since last fire) -// -// timeMachine.travel(by: 60 * 60 * 14) -// pixelKit.fire(event, frequency: .dailyOnly) // Fired (24 hours since last fire) -// -// // Wait for expectations to be fulfilled -// wait(for: [fireCallbackCalled], timeout: 0.5) -// } + /// Test firing a daily pixel a few times + func testDailyPixelFrequency() { + // Prepare test parameters + let appVersion = "1.0.5" + let headers = ["a": "2", "b": "3", "c": "2000"] + let log = OSLog(subsystem: "TestSubsystem", category: "TestCategory") + let event = TestEvent.dailyEvent + let userDefaults = userDefaults() + + let timeMachine = TimeMachine() + + // Set expectations + let fireCallbackCalled = expectation(description: "Expect the pixel firing callback to be called") + fireCallbackCalled.expectedFulfillmentCount = 3 + fireCallbackCalled.assertForOverFulfill = true + + // Prepare mock to validate expectations + let pixelKit = PixelKit(dryRun: false, + appVersion: appVersion, + defaultHeaders: headers, + log: log, + dailyPixelCalendar: nil, + dateGenerator: timeMachine.now, + defaults: userDefaults) { _, _, _, _, _, _ in + fireCallbackCalled.fulfill() + } + + // Run test + pixelKit.fire(event, frequency: .dailyOnly) // Fired + + timeMachine.travel(by: 60 * 60 * 2) + pixelKit.fire(event, frequency: .dailyOnly) // Skipped (2 hours since last fire) + + timeMachine.travel(by: 60 * 60 * 24 + 1000) + pixelKit.fire(event, frequency: .dailyOnly) // Fired (24 hours + 1000 seconds since last fire) + + timeMachine.travel(by: 60 * 60 * 10) + pixelKit.fire(event, frequency: .dailyOnly) // Skipped (10 hours since last fire) + + timeMachine.travel(by: 60 * 60 * 14) + pixelKit.fire(event, frequency: .dailyOnly) // Fired (24 hours since last fire) + + // Wait for expectations to be fulfilled + wait(for: [fireCallbackCalled], timeout: 0.5) + } /// Test firing a unique pixel func testUniquePixel() { @@ -321,7 +321,7 @@ final class PixelKitTests: XCTestCase { } private class TimeMachine { - private var date = Calendar.current.startOfDay(for: Date()) + private var date = Date() func travel(by timeInterval: TimeInterval) { date = date.addingTimeInterval(timeInterval) From e68375bf06332a637f5ecc618c75d1b74384175b Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Thu, 14 Dec 2023 12:20:44 -0800 Subject: [PATCH 5/9] Remove DBP test target (#1961) Task/Issue URL: https://app.asana.com/0/1199230911884351/1206156415929478/f Tech Design URL: CC: @jotaemepereira Description: This PR removes the DBP test target. The target wasn't being run on CI, and I've moved the one file it contained into the main app test suite and gotten it running again, so we have tests for pixel integration once more. --- Configuration/Tests/DBPUnitTests.xcconfig | 25 ---- Configuration/Tests/UnitTests.xcconfig | 2 +- DuckDuckGo.xcodeproj/project.pbxproj | 140 ++++-------------- .../DataBrokerProtectionPixelTests.swift | 6 +- 4 files changed, 34 insertions(+), 139 deletions(-) delete mode 100644 Configuration/Tests/DBPUnitTests.xcconfig rename {DuckDuckGoDBPTests => LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests}/DataBrokerProtectionPixelTests.swift (98%) diff --git a/Configuration/Tests/DBPUnitTests.xcconfig b/Configuration/Tests/DBPUnitTests.xcconfig deleted file mode 100644 index 1852b3c454..0000000000 --- a/Configuration/Tests/DBPUnitTests.xcconfig +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright © 2022 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. -// - -#include "TestsTargetsBase.xcconfig" - -ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES - -FEATURE_FLAGS = FEEDBACK NETWORK_PROTECTION - -INFOPLIST_FILE = DuckDuckGoDBPTests/Info.plist -PRODUCT_BUNDLE_IDENTIFIER = com.duckduckgo.macos.browser.DuckDuckGoDBPTests - -TEST_HOST=$(BUILT_PRODUCTS_DIR)/DuckDuckGoDBP.app/Contents/MacOS/DuckDuckGoDBP diff --git a/Configuration/Tests/UnitTests.xcconfig b/Configuration/Tests/UnitTests.xcconfig index e81034ac32..de9f4010a0 100644 --- a/Configuration/Tests/UnitTests.xcconfig +++ b/Configuration/Tests/UnitTests.xcconfig @@ -17,7 +17,7 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES -FEATURE_FLAGS = FEEDBACK NETWORK_PROTECTION +FEATURE_FLAGS = FEEDBACK NETWORK_PROTECTION DBP INFOPLIST_FILE = UnitTests/Info.plist PRODUCT_BUNDLE_IDENTIFIER = com.duckduckgo.macos.browser.DuckDuckGoTests diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index f7ce33110d..75b96e706e 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -1215,6 +1215,10 @@ 4B78A86B26BB3ADD0071BB16 /* BrowserImportSummaryViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B78A86A26BB3ADD0071BB16 /* BrowserImportSummaryViewController.swift */; }; 4B7A57CF279A4EF300B1C70E /* ChromePreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B7A57CE279A4EF300B1C70E /* ChromePreferences.swift */; }; 4B7A60A1273E0BE400BBDFEB /* WKWebsiteDataStoreExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B7A60A0273E0BE400BBDFEB /* WKWebsiteDataStoreExtension.swift */; }; + 4B81AD322B29507300706C96 /* DataBrokerProtectionPixelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B81AD312B29507300706C96 /* DataBrokerProtectionPixelTests.swift */; }; + 4B81AD332B29507300706C96 /* DataBrokerProtectionPixelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B81AD312B29507300706C96 /* DataBrokerProtectionPixelTests.swift */; }; + 4B81AD352B29512B00706C96 /* PixelKitTestingUtilities in Frameworks */ = {isa = PBXBuildFile; productRef = 4B81AD342B29512B00706C96 /* PixelKitTestingUtilities */; }; + 4B81AD372B29513100706C96 /* PixelKitTestingUtilities in Frameworks */ = {isa = PBXBuildFile; productRef = 4B81AD362B29513100706C96 /* PixelKitTestingUtilities */; }; 4B85A48028821CC500FC4C39 /* NSPasteboardItemExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B85A47F28821CC500FC4C39 /* NSPasteboardItemExtension.swift */; }; 4B8A4DFF27C83B29005F40E8 /* SaveIdentityViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8A4DFE27C83B29005F40E8 /* SaveIdentityViewController.swift */; }; 4B8A4E0127C8447E005F40E8 /* SaveIdentityPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B8A4E0027C8447E005F40E8 /* SaveIdentityPopover.swift */; }; @@ -2173,7 +2177,6 @@ 7B1E819E27C8874900FF0E60 /* ContentOverlayPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1E819B27C8874900FF0E60 /* ContentOverlayPopover.swift */; }; 7B1E819F27C8874900FF0E60 /* ContentOverlay.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7B1E819C27C8874900FF0E60 /* ContentOverlay.storyboard */; }; 7B1E81A027C8874900FF0E60 /* ContentOverlayViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1E819D27C8874900FF0E60 /* ContentOverlayViewController.swift */; }; - 7B20D5C62ADFEC6E0053C42A /* PixelKitTestingUtilities in Frameworks */ = {isa = PBXBuildFile; productRef = 7B20D5C52ADFEC6E0053C42A /* PixelKitTestingUtilities */; }; 7B2DDCF82A93A8BB0039D884 /* NetworkProtectionAppEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B2DDCF72A93A8BB0039D884 /* NetworkProtectionAppEvents.swift */; }; 7B2DDCFA2A93B25F0039D884 /* KeychainType+ClientDefault.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BD3AF5C2A8E7AF1006F9F56 /* KeychainType+ClientDefault.swift */; }; 7B2DDCFB2A93B25F0039D884 /* KeychainType+ClientDefault.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BD3AF5C2A8E7AF1006F9F56 /* KeychainType+ClientDefault.swift */; }; @@ -2189,7 +2192,6 @@ 7B5F9A752AE2BE4E002AEBC0 /* PixelKit in Frameworks */ = {isa = PBXBuildFile; productRef = 7B5F9A742AE2BE4E002AEBC0 /* PixelKit */; }; 7B8C083C2AE1268E00F4C67F /* PixelKit in Frameworks */ = {isa = PBXBuildFile; productRef = 7B8C083B2AE1268E00F4C67F /* PixelKit */; }; 7B934C412A866DD400FC8F9C /* UserDefaults+NetworkProtectionShared.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B934C402A866DD400FC8F9C /* UserDefaults+NetworkProtectionShared.swift */; }; - 7B96D0DC2ADFDB8E007E02C8 /* DataBrokerProtectionPixelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B96D0DB2ADFDB8E007E02C8 /* DataBrokerProtectionPixelTests.swift */; }; 7BA4727D26F01BC400EAA165 /* CoreDataTestUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B9292C42667104B00AD2C21 /* CoreDataTestUtilities.swift */; }; 7BA59C9B2AE18B49009A97B1 /* SystemExtensionManager in Frameworks */ = {isa = PBXBuildFile; productRef = 7BA59C9A2AE18B49009A97B1 /* SystemExtensionManager */; }; 7BA7CC392AD11E2D0042E5CE /* DuckDuckGoVPNAppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BA7CC0E2AD11DC80042E5CE /* DuckDuckGoVPNAppDelegate.swift */; }; @@ -2245,8 +2247,6 @@ 7BEEA5122AD1235B00A9E72B /* NetworkProtectionIPC in Frameworks */ = {isa = PBXBuildFile; productRef = 7BEEA5112AD1235B00A9E72B /* NetworkProtectionIPC */; }; 7BEEA5142AD1236300A9E72B /* NetworkProtectionIPC in Frameworks */ = {isa = PBXBuildFile; productRef = 7BEEA5132AD1236300A9E72B /* NetworkProtectionIPC */; }; 7BEEA5162AD1236E00A9E72B /* NetworkProtectionUI in Frameworks */ = {isa = PBXBuildFile; productRef = 7BEEA5152AD1236E00A9E72B /* NetworkProtectionUI */; }; - 7BF1A9D82AE054D300FCA683 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7BF1A9D72AE054D300FCA683 /* Info.plist */; }; - 7BF1A9DC2AE0551C00FCA683 /* DBPUnitTests.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 7BF1A9DB2AE0551C00FCA683 /* DBPUnitTests.xcconfig */; }; 7BFCB74E2ADE7E1A00DA3EA7 /* PixelKit in Frameworks */ = {isa = PBXBuildFile; productRef = 7BFCB74D2ADE7E1A00DA3EA7 /* PixelKit */; }; 7BFCB7502ADE7E2300DA3EA7 /* PixelKit in Frameworks */ = {isa = PBXBuildFile; productRef = 7BFCB74F2ADE7E2300DA3EA7 /* PixelKit */; }; 7BFE95522A9DF1CE0081ABE9 /* NetworkProtectionWaitlistFeatureFlagOverridesMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7BFE95512A9DF1CE0081ABE9 /* NetworkProtectionWaitlistFeatureFlagOverridesMenu.swift */; }; @@ -3411,6 +3411,7 @@ 4B78A86A26BB3ADD0071BB16 /* BrowserImportSummaryViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BrowserImportSummaryViewController.swift; sourceTree = ""; }; 4B7A57CE279A4EF300B1C70E /* ChromePreferences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChromePreferences.swift; sourceTree = ""; }; 4B7A60A0273E0BE400BBDFEB /* WKWebsiteDataStoreExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKWebsiteDataStoreExtension.swift; sourceTree = ""; }; + 4B81AD312B29507300706C96 /* DataBrokerProtectionPixelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = DataBrokerProtectionPixelTests.swift; path = LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionPixelTests.swift; sourceTree = SOURCE_ROOT; }; 4B85A47F28821CC500FC4C39 /* NSPasteboardItemExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSPasteboardItemExtension.swift; sourceTree = ""; }; 4B8A4DFE27C83B29005F40E8 /* SaveIdentityViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveIdentityViewController.swift; sourceTree = ""; }; 4B8A4E0027C8447E005F40E8 /* SaveIdentityPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveIdentityPopover.swift; sourceTree = ""; }; @@ -3603,8 +3604,6 @@ 7B76E6852AD5D77600186A84 /* XPCHelper */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = XPCHelper; sourceTree = ""; }; 7B934C3D2A866CFF00FC8F9C /* NetworkProtectionOnboardingMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkProtectionOnboardingMenu.swift; sourceTree = ""; }; 7B934C402A866DD400FC8F9C /* UserDefaults+NetworkProtectionShared.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+NetworkProtectionShared.swift"; sourceTree = ""; }; - 7B96D0CF2ADFDA7E007E02C8 /* DuckDuckGoDBPTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = DuckDuckGoDBPTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 7B96D0DB2ADFDB8E007E02C8 /* DataBrokerProtectionPixelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataBrokerProtectionPixelTests.swift; sourceTree = ""; }; 7BA7CC0B2AD11D1E0042E5CE /* DuckDuckGoVPNAppStore.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DuckDuckGoVPNAppStore.xcconfig; sourceTree = ""; }; 7BA7CC0C2AD11D1E0042E5CE /* DuckDuckGoVPN.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DuckDuckGoVPN.xcconfig; sourceTree = ""; }; 7BA7CC0E2AD11DC80042E5CE /* DuckDuckGoVPNAppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DuckDuckGoVPNAppDelegate.swift; sourceTree = ""; }; @@ -3632,7 +3631,6 @@ 7BEC20402B0F505F00243D3E /* BookmarkAddPopoverViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkAddPopoverViewController.swift; sourceTree = ""; }; 7BEC20412B0F505F00243D3E /* BookmarkAddFolderPopoverViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarkAddFolderPopoverViewController.swift; sourceTree = ""; }; 7BF1A9D72AE054D300FCA683 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 7BF1A9DB2AE0551C00FCA683 /* DBPUnitTests.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = DBPUnitTests.xcconfig; sourceTree = ""; }; 7BFE95512A9DF1CE0081ABE9 /* NetworkProtectionWaitlistFeatureFlagOverridesMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkProtectionWaitlistFeatureFlagOverridesMenu.swift; sourceTree = ""; }; 7BFE95532A9DF2930081ABE9 /* UserDefaults+NetworkProtectionWaitlist.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UserDefaults+NetworkProtectionWaitlist.swift"; sourceTree = ""; }; 85012B0129133F9F003D0DCC /* NavigationBarPopovers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarPopovers.swift; sourceTree = ""; }; @@ -4243,6 +4241,7 @@ files = ( 3706FE88293F661700E42796 /* OHHTTPStubs in Frameworks */, 3706FE89293F661700E42796 /* OHHTTPStubsSwift in Frameworks */, + 4B81AD372B29513100706C96 /* PixelKitTestingUtilities in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4361,14 +4360,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 7B96D0CC2ADFDA7E007E02C8 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 7B20D5C62ADFEC6E0053C42A /* PixelKitTestingUtilities in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 9D9AE8C62AAA39A70026E7DC /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -4428,6 +4419,7 @@ files = ( B6DA44172616C13800DD1EC2 /* OHHTTPStubs in Frameworks */, B6DA44192616C13800DD1EC2 /* OHHTTPStubsSwift in Frameworks */, + 4B81AD352B29512B00706C96 /* PixelKitTestingUtilities in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -4767,7 +4759,6 @@ children = ( 373A26962964CF0B0043FC57 /* TestsTargetsBase.xcconfig */, 378B588D295CF447002C0CC0 /* UnitTests.xcconfig */, - 7BF1A9DB2AE0551C00FCA683 /* DBPUnitTests.xcconfig */, 378B58C8295CF9A7002C0CC0 /* IntegrationTests.xcconfig */, 37E75733296F4F0500E1C162 /* UnitTestsAppStore.xcconfig */, 37E75734296F4F0500E1C162 /* IntegrationTestsAppStore.xcconfig */, @@ -5390,6 +5381,14 @@ path = DataExport; sourceTree = ""; }; + 4B81AD302B29506300706C96 /* DataBrokerProtection */ = { + isa = PBXGroup; + children = ( + 4B81AD312B29507300706C96 /* DataBrokerProtectionPixelTests.swift */, + ); + path = DataBrokerProtection; + sourceTree = ""; + }; 4B82E9B725B6A04B00656FE7 /* ContentBlocker */ = { isa = PBXGroup; children = ( @@ -5809,7 +5808,6 @@ isa = PBXGroup; children = ( 7BF1A9D72AE054D300FCA683 /* Info.plist */, - 7B96D0DB2ADFDB8E007E02C8 /* DataBrokerProtectionPixelTests.swift */, ); path = DuckDuckGoDBPTests; sourceTree = ""; @@ -6358,7 +6356,6 @@ 9D9AE8D12AAA39A70026E7DC /* DuckDuckGoDBPBackgroundAgent.app */, 9D9AE8F22AAA39D30026E7DC /* .app */, 4B957C412AC7AE700062CA31 /* DuckDuckGo Privacy Pro.app */, - 7B96D0CF2ADFDA7E007E02C8 /* DuckDuckGoDBPTests.xctest */, ); name = Products; sourceTree = ""; @@ -6439,6 +6436,7 @@ AA585D93248FD31400E9A3E2 /* UnitTests */ = { isa = PBXGroup; children = ( + 4B81AD302B29506300706C96 /* DataBrokerProtection */, 5629846D2AC460DF00AC20EB /* Sync */, B6A5A28C25B962CB00AA7ADA /* App */, 85F1B0C725EF9747004792B6 /* AppDelegate */, @@ -7930,6 +7928,7 @@ packageProductDependencies = ( 3706FDD6293F661700E42796 /* OHHTTPStubs */, 3706FDD8293F661700E42796 /* OHHTTPStubsSwift */, + 4B81AD362B29513100706C96 /* PixelKitTestingUtilities */, ); productName = DuckDuckGoTests; productReference = 3706FE99293F661700E42796 /* Unit Tests App Store.xctest */; @@ -8168,27 +8167,6 @@ productReference = 7B4CE8DA26F02108009134B1 /* UI Tests.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; - 7B96D0CE2ADFDA7E007E02C8 /* DuckDuckGoDBPTests */ = { - isa = PBXNativeTarget; - buildConfigurationList = 7B96D0D52ADFDA7F007E02C8 /* Build configuration list for PBXNativeTarget "DuckDuckGoDBPTests" */; - buildPhases = ( - 7B96D0CB2ADFDA7E007E02C8 /* Sources */, - 7B96D0CC2ADFDA7E007E02C8 /* Frameworks */, - 7B96D0CD2ADFDA7E007E02C8 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - 7B20D5C82ADFEC730053C42A /* PBXTargetDependency */, - ); - name = DuckDuckGoDBPTests; - packageProductDependencies = ( - 7B20D5C52ADFEC6E0053C42A /* PixelKitTestingUtilities */, - ); - productName = DuckDuckGoDBPTests; - productReference = 7B96D0CF2ADFDA7E007E02C8 /* DuckDuckGoDBPTests.xctest */; - productType = "com.apple.product-type.bundle.unit-test"; - }; 9D9AE8B22AAA39A70026E7DC /* DuckDuckGoDBPBackgroundAgent */ = { isa = PBXNativeTarget; buildConfigurationList = 9D9AE8CC2AAA39A70026E7DC /* Build configuration list for PBXNativeTarget "DuckDuckGoDBPBackgroundAgent" */; @@ -8302,6 +8280,7 @@ packageProductDependencies = ( B6DA44162616C13800DD1EC2 /* OHHTTPStubs */, B6DA44182616C13800DD1EC2 /* OHHTTPStubsSwift */, + 4B81AD342B29512B00706C96 /* PixelKitTestingUtilities */, ); productName = DuckDuckGoTests; productReference = AA585D90248FD31400E9A3E2 /* Unit Tests.xctest */; @@ -8368,10 +8347,6 @@ CreatedOnToolsVersion = 12.5.1; TestTargetID = AA585D7D248FD31100E9A3E2; }; - 7B96D0CE2ADFDA7E007E02C8 = { - CreatedOnToolsVersion = 15.0; - TestTargetID = 31929F7B2A4C4CFF0084EA89; - }; AA585D7D248FD31100E9A3E2 = { CreatedOnToolsVersion = 11.5; }; @@ -8418,7 +8393,6 @@ 4B4BEC1F2A11B4E2001D9AC5 /* DuckDuckGoNotifications */, 4B2D06382A11CFBA00DE1F49 /* DuckDuckGoVPN */, 4B2D06682A13318400DE1F49 /* DuckDuckGoVPNAppStore */, - 7B96D0CE2ADFDA7E007E02C8 /* DuckDuckGoDBPTests */, 9D9AE8B22AAA39A70026E7DC /* DuckDuckGoDBPBackgroundAgent */, 9D9AE8D32AAA39D30026E7DC /* DuckDuckGoDBPBackgroundAgentAppStore */, 4B9579252AC7AE700062CA31 /* DuckDuckGo Privacy Pro */, @@ -8635,15 +8609,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 7B96D0CD2ADFDA7E007E02C8 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 7BF1A9D82AE054D300FCA683 /* Info.plist in Resources */, - 7BF1A9DC2AE0551C00FCA683 /* DBPUnitTests.xcconfig in Resources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 9D9AE8C92AAA39A70026E7DC /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -9815,6 +9780,7 @@ 3706FE00293F661700E42796 /* FaviconStoringMock.swift in Sources */, 3706FE01293F661700E42796 /* PixelStoreMock.swift in Sources */, 3706FE02293F661700E42796 /* BookmarksBarViewModelTests.swift in Sources */, + 4B81AD332B29507300706C96 /* DataBrokerProtectionPixelTests.swift in Sources */, 3706FE03293F661700E42796 /* CoreDataStoreTests.swift in Sources */, 3706FE04293F661700E42796 /* TreeControllerTests.swift in Sources */, 3706FE05293F661700E42796 /* DownloadsWebViewMock.m in Sources */, @@ -10879,14 +10845,6 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 7B96D0CB2ADFDA7E007E02C8 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 7B96D0DC2ADFDB8E007E02C8 /* DataBrokerProtectionPixelTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; 9D9AE8B62AAA39A70026E7DC /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -11674,6 +11632,7 @@ 4B723E0926B0003E00E14D75 /* CSVLoginExporterTests.swift in Sources */, B6DA06E12913AEDC00225DE2 /* TestNavigationDelegate.swift in Sources */, 56D145E829E6BB6300E3488A /* CapturingDataImportProvider.swift in Sources */, + 4B81AD322B29507300706C96 /* DataBrokerProtectionPixelTests.swift in Sources */, B630793526731BC400DCEE41 /* URLSuggestedFilenameTests.swift in Sources */, B603974E29C1F93600902A34 /* TabPermissionsTests.swift in Sources */, 85AC3B4925DAC9BD00C7D2AA /* ConfigurationStorageTests.swift in Sources */, @@ -11886,10 +11845,6 @@ isa = PBXTargetDependency; productRef = 4B5F14FB2A15291D0060320F /* InputFilesChecker */; }; - 7B20D5C82ADFEC730053C42A /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - productRef = 7B20D5C72ADFEC730053C42A /* PixelKitTestingUtilities */; - }; 7B4CE8E026F02108009134B1 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = AA585D7D248FD31100E9A3E2 /* DuckDuckGo Privacy Browser */; @@ -12276,34 +12231,6 @@ }; name = Release; }; - 7B96D0D62ADFDA7F007E02C8 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7BF1A9DB2AE0551C00FCA683 /* DBPUnitTests.xcconfig */; - buildSettings = { - }; - name = Debug; - }; - 7B96D0D72ADFDA7F007E02C8 /* CI */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7BF1A9DB2AE0551C00FCA683 /* DBPUnitTests.xcconfig */; - buildSettings = { - }; - name = CI; - }; - 7B96D0D82ADFDA7F007E02C8 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7BF1A9DB2AE0551C00FCA683 /* DBPUnitTests.xcconfig */; - buildSettings = { - }; - name = Release; - }; - 7B96D0D92ADFDA7F007E02C8 /* Review */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 7BF1A9DB2AE0551C00FCA683 /* DBPUnitTests.xcconfig */; - buildSettings = { - }; - name = Review; - }; 9D9AE8CD2AAA39A70026E7DC /* Debug */ = { isa = XCBuildConfiguration; baseConfigurationReference = 7B6EC5E52AE2D8AF004FE6DF /* DuckDuckGoDBPAgent.xcconfig */; @@ -12589,17 +12516,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 7B96D0D52ADFDA7F007E02C8 /* Build configuration list for PBXNativeTarget "DuckDuckGoDBPTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 7B96D0D62ADFDA7F007E02C8 /* Debug */, - 7B96D0D72ADFDA7F007E02C8 /* CI */, - 7B96D0D82ADFDA7F007E02C8 /* Release */, - 7B96D0D92ADFDA7F007E02C8 /* Review */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 9D9AE8CC2AAA39A70026E7DC /* Build configuration list for PBXNativeTarget "DuckDuckGoDBPBackgroundAgent" */ = { isa = XCConfigurationList; buildConfigurations = ( @@ -12972,6 +12888,14 @@ isa = XCSwiftPackageProductDependency; productName = "plugin:InputFilesChecker"; }; + 4B81AD342B29512B00706C96 /* PixelKitTestingUtilities */ = { + isa = XCSwiftPackageProductDependency; + productName = PixelKitTestingUtilities; + }; + 4B81AD362B29513100706C96 /* PixelKitTestingUtilities */ = { + isa = XCSwiftPackageProductDependency; + productName = PixelKitTestingUtilities; + }; 4B8F52342A169D2D00BE7131 /* Common */ = { isa = XCSwiftPackageProductDependency; package = 9807F643278CA16F00E1547B /* XCRemoteSwiftPackageReference "BrowserServicesKit" */; @@ -13080,14 +13004,6 @@ isa = XCSwiftPackageProductDependency; productName = Purchase; }; - 7B20D5C52ADFEC6E0053C42A /* PixelKitTestingUtilities */ = { - isa = XCSwiftPackageProductDependency; - productName = PixelKitTestingUtilities; - }; - 7B20D5C72ADFEC730053C42A /* PixelKitTestingUtilities */ = { - isa = XCSwiftPackageProductDependency; - productName = PixelKitTestingUtilities; - }; 7B31FD8B2AD125620086AA24 /* NetworkProtectionIPC */ = { isa = XCSwiftPackageProductDependency; productName = NetworkProtectionIPC; diff --git a/DuckDuckGoDBPTests/DataBrokerProtectionPixelTests.swift b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionPixelTests.swift similarity index 98% rename from DuckDuckGoDBPTests/DataBrokerProtectionPixelTests.swift rename to LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionPixelTests.swift index 212606714a..1d32a2e762 100644 --- a/DuckDuckGoDBPTests/DataBrokerProtectionPixelTests.swift +++ b/LocalPackages/DataBrokerProtection/Tests/DataBrokerProtectionTests/DataBrokerProtectionPixelTests.swift @@ -16,13 +16,15 @@ // limitations under the License. // +#if DBP + import DataBrokerProtection import Networking import Foundation import PixelKit import PixelKitTestingUtilities import XCTest -@testable import DuckDuckGo_DBP +@testable import DuckDuckGo_Privacy_Browser /// Tests to ensure that DBP pixels sent from the main app work well /// @@ -143,3 +145,5 @@ final class DataBrokerProtectionPixelTests: XCTestCase { } } } + +#endif From 718a50c2bac891f0aa2e97950d60d4fe8f061c97 Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Thu, 14 Dec 2023 23:01:48 -0800 Subject: [PATCH 6/9] Use static date for PixelKit tests (#1973) Task/Issue URL: https://app.asana.com/0/1199230911884351/1206177144674653/f Tech Design URL: CC: @ayoy @quanganhdo Description: This PR changes the date used by the PixelKit tests to be static. --- LocalPackages/PixelKit/Tests/PixelKitTests/PixelKitTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LocalPackages/PixelKit/Tests/PixelKitTests/PixelKitTests.swift b/LocalPackages/PixelKit/Tests/PixelKitTests/PixelKitTests.swift index 8450c0df9c..b9576d6a6a 100644 --- a/LocalPackages/PixelKit/Tests/PixelKitTests/PixelKitTests.swift +++ b/LocalPackages/PixelKit/Tests/PixelKitTests/PixelKitTests.swift @@ -321,7 +321,7 @@ final class PixelKitTests: XCTestCase { } private class TimeMachine { - private var date = Date() + private var date = Date(timeIntervalSince1970: 0) func travel(by timeInterval: TimeInterval) { date = date.addingTimeInterval(timeInterval) From bd447bfa341b25e6207b83a81aaf6b47868d2a99 Mon Sep 17 00:00:00 2001 From: Sam Symons Date: Fri, 15 Dec 2023 01:13:28 -0800 Subject: [PATCH 7/9] Move DBP tests into main target (#1974) Task/Issue URL: https://app.asana.com/0/1199230911884351/1206177175303794/f Description: This PR removes the dedicated DBP workflow matrix entry, and makes the DBP tests run with the main app scheme, like the rest of our local packages. --- .github/workflows/pr.yml | 15 +++--------- .../DuckDuckGo Privacy Browser.xcscheme | 24 +++++++++++++++++++ .../DataBrokerProtectionProfileTests.swift | 2 +- 3 files changed, 28 insertions(+), 13 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index b3400f2850..df2b6ccea9 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -54,32 +54,24 @@ jobs: strategy: matrix: - flavor: [ "Sandbox", "Non-Sandbox", "DBP" ] + flavor: [ "Sandbox", "Non-Sandbox" ] include: - scheme: DuckDuckGo Privacy Browser flavor: Non-Sandbox - scheme: DuckDuckGo Privacy Browser App Store flavor: Sandbox - - scheme: DataBrokerProtectionTests - flavor: DBP - active-arch: YES flavor: Non-Sandbox - active-arch: NO flavor: Sandbox - - active-arch: YES - flavor: DBP - integration-tests-target: Integration Tests flavor: Non-Sandbox - integration-tests-target: Integration Tests App Store flavor: Sandbox - - integration-tests-target: Integration Tests - flavor: DBP - cache-key: flavor: Non-Sandbox - cache-key: sandbox- flavor: Sandbox - - cache-key: dbp- - flavor: DBP runs-on: macos-13-xlarge timeout-minutes: 30 @@ -144,7 +136,6 @@ jobs: || { mv "$(grep -m 1 '.*\.xcresult' ${{ matrix.flavor }}-unittests-xcodebuild.log | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" ./${{ matrix.flavor }}-unittests.xcresult && exit 1; } - name: Run integration tests - if: matrix.flavor != 'DBP' run: | set -o pipefail && xcodebuild test \ -scheme "${{ matrix.scheme }}" \ @@ -222,7 +213,7 @@ jobs: - name: Upload failed integration tests log uses: actions/upload-artifact@v3 - if: failure() && matrix.flavor != 'DBP' + if: failure() with: name: ${{ matrix.flavor }}-integrationtests-xcodebuild.log path: ${{ matrix.flavor }}-integrationtests-xcodebuild.log @@ -230,7 +221,7 @@ jobs: - name: Upload failed integration tests xcresult uses: actions/upload-artifact@v3 - if: failure() && matrix.flavor != 'DBP' + if: failure() with: name: ${{ matrix.flavor }}-integrationtests.xcresult path: ${{ matrix.flavor }}-integrationtests.xcresult diff --git a/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/DuckDuckGo Privacy Browser.xcscheme b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/DuckDuckGo Privacy Browser.xcscheme index 77b2c34fba..eaa06a07ec 100644 --- a/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/DuckDuckGo Privacy Browser.xcscheme +++ b/DuckDuckGo.xcodeproj/xcshareddata/xcschemes/DuckDuckGo Privacy Browser.xcscheme @@ -48,6 +48,20 @@ ReferencedContainer = "container:DuckDuckGo.xcodeproj"> + + + + + + + + Date: Fri, 15 Dec 2023 11:23:30 +0100 Subject: [PATCH 8/9] Add GHA workflow to cut release branch (#1976) Task/Issue URL: https://app.asana.com/0/1203301625297703/1206152126256397/f Description: Add code_freeze.yml that takes no arguments and must be run from main branch. It evaluates next release version number, creates a release branch, updates version and embedded files, creates a new release task in Asana, assigns the task to the actor (person running the workflow), runs PR checks workflow, updates version number, prepares App Store and DMG builds and uploads artifacts to Asana task. --- .github/workflows/build_appstore.yml | 14 ++- .github/workflows/build_notarized.yml | 14 ++- .github/workflows/code_freeze.yml | 140 ++++++++++++++++++++++++++ .github/workflows/pr.yml | 19 ++-- .github/workflows/release.yml | 6 ++ fastlane/Fastfile | 56 ++++++++++- fastlane/README.md | 16 +++ 7 files changed, 243 insertions(+), 22 deletions(-) create mode 100644 .github/workflows/code_freeze.yml diff --git a/.github/workflows/build_appstore.yml b/.github/workflows/build_appstore.yml index 3ed67cdd61..fe5fdb9add 100644 --- a/.github/workflows/build_appstore.yml +++ b/.github/workflows/build_appstore.yml @@ -26,6 +26,10 @@ on: description: "Asana release task URL" required: true type: string + branch: + description: "Branch name" + required: false + type: string secrets: SSH_PRIVATE_KEY_FASTLANE_MATCH: required: true @@ -60,9 +64,9 @@ jobs: - name: Assert release branch if: env.destination == 'appstore' run: | - case "${{ github.ref }}" in - refs/heads/release/*) ;; - refs/heads/hotfix/*) ;; + case "${{ inputs.branch || github.ref_name }}" in + release/*) ;; + hotfix/*) ;; *) echo "👎 Not a release or hotfix branch"; exit 1 ;; esac @@ -76,7 +80,7 @@ jobs: uses: actions/checkout@v3 with: submodules: recursive - ref: ${{ github.ref_name }} + ref: ${{ inputs.branch || github.ref_name }} - name: Select Xcode run: sudo xcode-select -s /Applications/Xcode_$(<.xcode-version).app/Contents/Developer @@ -104,7 +108,7 @@ jobs: echo "app_version=${version}.${build_number}" >> $GITHUB_ENV - name: Upload dSYMs artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: DuckDuckGo-${{ env.destination }}-dSYM-${{ env.app_version }} path: ${{ env.dsyms_path }} diff --git a/.github/workflows/build_notarized.yml b/.github/workflows/build_notarized.yml index e6486a1877..99fd243a0a 100644 --- a/.github/workflows/build_notarized.yml +++ b/.github/workflows/build_notarized.yml @@ -38,6 +38,10 @@ on: description: "Asana release task URL" required: true type: string + branch: + description: "Branch name" + required: false + type: string secrets: BUILD_CERTIFICATE_BASE64: required: true @@ -103,7 +107,7 @@ jobs: uses: actions/checkout@v3 with: submodules: recursive - ref: ${{ github.ref_name }} + ref: ${{ inputs.branch || github.ref_name }} - name: Install Apple Developer ID Application certificate uses: ./.github/actions/install-certs-and-profiles @@ -155,13 +159,13 @@ jobs: echo "app-name=${{ env.app-name }}" >> $GITHUB_OUTPUT - name: Upload app artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: DuckDuckGo-${{ env.release-type }}-${{ env.app-version }}.app path: ${{ github.workspace }}/release/DuckDuckGo-${{ env.app-version }}.zip - name: Upload dSYMs artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: DuckDuckGo-${{ env.release-type }}-dSYM-${{ env.app-version }} path: ${{ github.workspace }}/release/DuckDuckGo-${{ env.app-version }}-dSYM.zip @@ -205,7 +209,7 @@ jobs: steps: - name: Fetch app bundle - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: DuckDuckGo-${{ env.release-type }}-${{ env.app-version }}.app path: ${{ github.workspace }}/dmg @@ -234,7 +238,7 @@ jobs: "dmg" - name: Upload DMG artifact - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: DuckDuckGo-${{ env.release-type }}-${{ env.app-version }}.dmg path: ${{ github.workspace }}/duckduckgo*-${{ env.app-version }}.dmg diff --git a/.github/workflows/code_freeze.yml b/.github/workflows/code_freeze.yml new file mode 100644 index 0000000000..7883f9200c --- /dev/null +++ b/.github/workflows/code_freeze.yml @@ -0,0 +1,140 @@ +name: Code Freeze + +on: + workflow_dispatch: + +jobs: + + create_release_branch: + + name: Create Release Branch + + runs-on: macos-13-xlarge + timeout-minutes: 10 + + outputs: + release_branch_name: ${{ steps.make_release_branch.outputs.release_branch_name }} + asana_task_url: ${{ steps.create_release_task.outputs.asana_task_url }} + + steps: + + - name: Assert main branch + run: | + if [ "${{ github.ref_name }}" != "main" ]; then + echo "👎 Not the main branch" + exit 1 + fi + + - name: Check out the code + uses: actions/checkout@v3 + with: + submodules: recursive + + - name: Prepare fastlane + run: bundle install + + - name: Make release branch + id: make_release_branch + env: + APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY_BASE64 }} + APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} + APPLE_API_KEY_ISSUER: ${{ secrets.APPLE_API_KEY_ISSUER }} + run: | + git config --global user.name "Dax the Duck" + git config --global user.email "dax@duckduckgo.com" + bundle exec fastlane make_release_branch + + - name: Create release task + id: create_release_task + env: + ASANA_ACCESS_TOKEN: ${{ secrets.ASANA_ACCESS_TOKEN }} + run: | + version="$(echo ${{ steps.make_release_branch.outputs.release_branch_name }} | cut -d '/' -f 2)" + task_name="macOS App Release $version" + asana_task_id="$(curl -fLSs -X POST "https://app.asana.com/api/1.0/task_templates/${{ vars.MACOS_RELEASE_TASK_TEMPLATE_ID }}/instantiateTask" \ + -H "Authorization: Bearer ${{ env.ASANA_ACCESS_TOKEN }}" \ + -H "Content-Type: application/json" \ + -d "{ \"data\": { \"name\": \"$task_name\" }}" \ + | jq -r .data.new_task.gid)" + echo "asana_task_url=https://app.asana.com/0/0/${asana_task_id}/f" >> $GITHUB_OUTPUT + + assignee_id="$(curl -fLSs https://raw.githubusercontent.com/duckduckgo/BrowserServicesKit/main/.github/actions/asana-failed-pr-checks/user_ids.json \ + | jq -r .${{ github.actor }})" + + curl -fLSs -X PUT "https://app.asana.com/api/1.0/tasks/${asana_task_id}" \ + -H "Authorization: Bearer ${{ env.ASANA_ACCESS_TOKEN }}" \ + -H "Content-Type: application/json" \ + --output /dev/null \ + -d "{ \"data\": { \"assignee\": \"$assignee_id\" }}" + + run_tests: + + name: Run Tests + + needs: create_release_branch + uses: ./.github/workflows/pr.yml + with: + branch: ${{ needs.create_release_branch.outputs.release_branch_name }} + secrets: + ASANA_ACCESS_TOKEN: ${{ secrets.ASANA_ACCESS_TOKEN }} + + increment_build_number: + + name: Increment Build Number + + needs: [ create_release_branch, run_tests ] + runs-on: macos-13-xlarge + timeout-minutes: 10 + + steps: + + - name: Check out the code + uses: actions/checkout@v3 + with: + submodules: recursive + ref: ${{ needs.create_release_branch.outputs.release_branch_name }} + + - name: Prepare fastlane + run: bundle install + + - name: Increment build number + env: + APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY_BASE64 }} + APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} + APPLE_API_KEY_ISSUER: ${{ secrets.APPLE_API_KEY_ISSUER }} + run: | + git config --global user.name "Dax the Duck" + git config --global user.email "dax@duckduckgo.com" + bundle exec fastlane bump_internal_release update_embedded_files:false + + prepare_release: + name: Prepare Release + needs: [ create_release_branch, increment_build_number ] + uses: ./.github/workflows/release.yml + with: + asana-task-url: ${{ needs.create_release_branch.outputs.asana_task_url }} + branch: ${{ needs.create_release_branch.outputs.release_branch_name }} + secrets: + BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} + P12_PASSWORD: ${{ secrets.P12_PASSWORD }} + KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} + REVIEW_PROVISION_PROFILE_BASE64: ${{ secrets.REVIEW_PROVISION_PROFILE_BASE64 }} + RELEASE_PROVISION_PROFILE_BASE64: ${{ secrets.RELEASE_PROVISION_PROFILE_BASE64 }} + DBP_AGENT_RELEASE_PROVISION_PROFILE_BASE64: ${{ secrets.DBP_AGENT_RELEASE_PROVISION_PROFILE_BASE64 }} + DBP_AGENT_REVIEW_PROVISION_PROFILE_BASE64: ${{ secrets.DBP_AGENT_REVIEW_PROVISION_PROFILE_BASE64 }} + NETP_SYSEX_RELEASE_PROVISION_PROFILE_BASE64_V2: ${{ secrets.NETP_SYSEX_RELEASE_PROVISION_PROFILE_BASE64_V2 }} + NETP_SYSEX_REVIEW_PROVISION_PROFILE_BASE64_V2: ${{ secrets.NETP_SYSEX_REVIEW_PROVISION_PROFILE_BASE64_V2 }} + NETP_AGENT_RELEASE_PROVISION_PROFILE_BASE64_V2: ${{ secrets.NETP_AGENT_RELEASE_PROVISION_PROFILE_BASE64_V2 }} + NETP_AGENT_REVIEW_PROVISION_PROFILE_BASE64_V2: ${{ secrets.NETP_AGENT_REVIEW_PROVISION_PROFILE_BASE64_V2 }} + NETP_NOTIFICATIONS_RELEASE_PROVISION_PROFILE_BASE64: ${{ secrets.NETP_NOTIFICATIONS_RELEASE_PROVISION_PROFILE_BASE64 }} + NETP_NOTIFICATIONS_REVIEW_PROVISION_PROFILE_BASE64: ${{ secrets.NETP_NOTIFICATIONS_REVIEW_PROVISION_PROFILE_BASE64 }} + APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY_BASE64 }} + APPLE_API_KEY_ID: ${{ secrets.APPLE_API_KEY_ID }} + APPLE_API_KEY_ISSUER: ${{ secrets.APPLE_API_KEY_ISSUER }} + ASANA_ACCESS_TOKEN: ${{ secrets.ASANA_ACCESS_TOKEN }} + MM_HANDLES_BASE64: ${{ secrets.MM_HANDLES_BASE64 }} + MM_WEBHOOK_URL: ${{ secrets.MM_WEBHOOK_URL }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }} + SSH_PRIVATE_KEY_FASTLANE_MATCH: ${{ secrets.SSH_PRIVATE_KEY_FASTLANE_MATCH }} diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index df2b6ccea9..4ad1e6db0a 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -5,6 +5,11 @@ on: branches: [ main, "release/**" ] pull_request: workflow_call: + inputs: + branch: + description: "Branch name" + required: false + type: string secrets: ASANA_ACCESS_TOKEN: required: true @@ -38,7 +43,7 @@ jobs: if: github.event_name != 'pull_request' && github.event_name != 'push' uses: actions/checkout@v3 with: - ref: ${{ github.ref_name }} + ref: ${{ inputs.branch || github.ref_name }} - name: Run ShellCheck uses: ludeeus/action-shellcheck@master @@ -92,7 +97,7 @@ jobs: uses: actions/checkout@v3 with: submodules: recursive - ref: ${{ github.ref_name }} + ref: ${{ inputs.branch || github.ref_name }} - name: Set cache key hash run: | @@ -196,7 +201,7 @@ jobs: | xargs -L 1 ./scripts/report-failed-unit-test.sh - name: Upload failed unit tests log - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: ${{ matrix.flavor }}-unittests-xcodebuild.log @@ -204,7 +209,7 @@ jobs: retention-days: 7 - name: Upload failed unit tests xcresult - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: ${{ matrix.flavor }}-unittests.xcresult @@ -212,7 +217,7 @@ jobs: retention-days: 7 - name: Upload failed integration tests log - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: ${{ matrix.flavor }}-integrationtests-xcodebuild.log @@ -220,7 +225,7 @@ jobs: retention-days: 7 - name: Upload failed integration tests xcresult - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: ${{ matrix.flavor }}-integrationtests.xcresult @@ -319,7 +324,7 @@ jobs: | xcbeautify - name: Upload failed test log - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 if: failure() with: name: release-xcodebuild.log diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 33b902609d..7ea14e28c8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,6 +13,10 @@ on: description: "Asana release task URL" required: true type: string + branch: + description: "Branch name" + required: false + type: string secrets: BUILD_CERTIFICATE_BASE64: required: true @@ -70,6 +74,7 @@ jobs: release-type: release create-dmg: true asana-task-url: ${{ github.event.inputs.asana-task-url || inputs.asana-task-url }} + branch: ${{ inputs.branch }} secrets: BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} P12_PASSWORD: ${{ secrets.P12_PASSWORD }} @@ -99,6 +104,7 @@ jobs: with: destination: appstore asana-task-url: ${{ github.event.inputs.asana-task-url || inputs.asana-task-url }} + branch: ${{ inputs.branch }} secrets: SSH_PRIVATE_KEY_FASTLANE_MATCH: ${{ secrets.SSH_PRIVATE_KEY_FASTLANE_MATCH }} APPLE_API_KEY_BASE64: ${{ secrets.APPLE_API_KEY_BASE64 }} diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 75e14c98df..5e67e77929 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -100,6 +100,41 @@ platform :mac do })) end + # Creates a new release branch and updates embedded files. + # + # - Cuts a new release branch + # - Updates submodules and embedded files + # - Pushes changes to remote + # + # @option [String] version (default: nil) Marketing version string + # + desc 'Executes the release preparation work in the repository' + lane :make_release_branch do |options| + begin + macos_codefreeze_prechecks + new_version = validate_new_version(options) + macos_create_release_branch(version: new_version) + macos_update_embedded_files + macos_update_version_config(version: new_version) + sh('git', 'push') + + sh("echo \"release_branch_name=#{RELEASE_BRANCH}/#{new_version}\" >> $GITHUB_OUTPUT") if is_ci + + rescue => exception + if exception.message == "Tests have failed" + UI.user_error! %{Tests have failed. +* If you believe the failing test is flaky, please retry the same fastlane command, + appending `resume:true`. +* If the failure looks legitimate, try to fix it, commit the fix (be sure to only + include the files you've changed while making a fix and leave other changed files + unmodified), and run the command again appending `resume:true`. + } + else + raise exception + end + end + end + # Executes the release preparation work in the repository # # - Cuts a new release branch @@ -500,10 +535,8 @@ release in progress and you're making a follow-up internal release that includes end end - unless is_ci - # Run tests (CI will run them separately) - run_tests(scheme: 'DuckDuckGo Privacy Browser') - end + # Run tests (CI will run them separately) + run_tests(scheme: 'DuckDuckGo Privacy Browser') unless is_ci # Every thing looks good: commit and push unless modified_files.empty? @@ -513,7 +546,7 @@ release in progress and you're making a follow-up internal release that includes end end - # Updates version in the config file + # Updates version and build number in respective config files # # @option [String] version Marketing version string # @option [String] build_number Build number @@ -532,6 +565,19 @@ release in progress and you're making a follow-up internal release that includes ) end + # Updates version in the config file + # + # @option [String] version Marketing version string + # + private_lane :macos_update_version_config do |options| + version = options[:version] + File.write(VERSION_CONFIG_PATH, "#{VERSION_CONFIG_DEFINITION} = #{version}\n") + git_commit( + path: VERSION_CONFIG_PATH, + message: "Set marketing version to #{version}" + ) + end + # Reads build number from the config file # # @return [String] build number read from the file, or nil in case of failure diff --git a/fastlane/README.md b/fastlane/README.md index bd417f28d9..13752cbc3f 100644 --- a/fastlane/README.md +++ b/fastlane/README.md @@ -47,6 +47,14 @@ Makes App Store release build and uploads it to App Store Connect Updates App Store metadata +### mac make_release_branch + +```sh +[bundle exec] fastlane mac make_release_branch +``` + +Executes the release preparation work in the repository + ### mac code_freeze ```sh @@ -71,6 +79,14 @@ Prepares new internal release on top of an existing one Executes the hotfix release preparation work in the repository +### mac update_embedded_files + +```sh +[bundle exec] fastlane mac update_embedded_files +``` + +Updates embedded files and pushes to remote. + ### mac set_version ```sh From 227da8ce62c6bcafdffacd28fe01acdce5df654f Mon Sep 17 00:00:00 2001 From: Alexey Martemyanov Date: Fri, 15 Dec 2023 17:58:55 +0600 Subject: [PATCH 9/9] drop Main.storyboard (#1944) Task/Issue URL: https://app.asana.com/0/1199230911884351/1206126728134278/f --- DuckDuckGo.xcodeproj/project.pbxproj | 56 ++--- .../Bookmarks/Model/BookmarkManager.swift | 14 +- .../Services/BookmarkStoreMock.swift | 32 ++- .../View/BookmarksBarViewController.swift | 12 +- DuckDuckGo/Common/Database/Database.swift | 4 +- .../NSLayoutConstraintExtension.swift} | 23 +- .../Common/Extensions/NSScreenExtension.swift | 2 + .../NSViewControllerExtension.swift | 6 +- .../Common/Extensions/NSViewExtension.swift | 17 +- DuckDuckGo/Common/View/AppKit/ColorView.swift | 10 +- .../Services/DownloadListStore.swift | 2 +- .../FindInPage/FindInPageViewController.swift | 4 + DuckDuckGo/Fire/View/FireViewController.swift | 10 +- ... AddEditFavoriteViewController.storyboard} | 65 +----- .../View/AddEditFavoriteViewController.swift | 10 + .../View/HomePageViewController.swift | 67 ++---- DuckDuckGo/MainWindow/Main.storyboard | 178 -------------- .../MainWindow/MainViewController.swift | 221 +++++++++++------- .../MainWindow/MainWindowController.swift | 9 +- DuckDuckGo/Menus/MainMenuActions.swift | 10 +- .../View/NavigationBarViewController.swift | 38 ++- .../Model/AutofillPreferencesModel.swift | 2 +- .../Preferences/Model/SyncPreferences.swift | 2 +- .../Tab/View/Base.lproj/BrowserTab.storyboard | 23 +- .../Tab/View/BrowserTabViewController.swift | 28 +-- .../TabBar/View/TabBarViewController.swift | 6 + DuckDuckGo/Windows/View/WindowsManager.swift | 21 +- 27 files changed, 351 insertions(+), 521 deletions(-) rename {UnitTests => DuckDuckGo}/Bookmarks/Services/BookmarkStoreMock.swift (68%) rename DuckDuckGo/{HomePage/View/AddEditFavoriteWindow.swift => Common/Extensions/NSLayoutConstraintExtension.swift} (53%) rename DuckDuckGo/HomePage/View/{HomePage.storyboard => AddEditFavoriteViewController.storyboard} (71%) delete mode 100644 DuckDuckGo/MainWindow/Main.storyboard diff --git a/DuckDuckGo.xcodeproj/project.pbxproj b/DuckDuckGo.xcodeproj/project.pbxproj index 75b96e706e..f08acde17e 100644 --- a/DuckDuckGo.xcodeproj/project.pbxproj +++ b/DuckDuckGo.xcodeproj/project.pbxproj @@ -216,7 +216,6 @@ 3706FACC293F65D500E42796 /* SaveCredentialsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8589063B267BCDC000D23B0D /* SaveCredentialsViewController.swift */; }; 3706FACD293F65D500E42796 /* PopUpButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBE0AA627B9B027003B37A8 /* PopUpButton.swift */; }; 3706FACE293F65D500E42796 /* SuggestionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AABEE6A424AA0A7F0043105B /* SuggestionViewController.swift */; }; - 3706FACF293F65D500E42796 /* AddEditFavoriteWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85589E7A27BBB8620038AD11 /* AddEditFavoriteWindow.swift */; }; 3706FAD1293F65D500E42796 /* VisitViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA7E919E287872EA00AB6B62 /* VisitViewModel.swift */; }; 3706FAD2293F65D500E42796 /* Atb.swift in Sources */ = {isa = PBXBuildFile; fileRef = B69B50352726A11F00758A2B /* Atb.swift */; }; 3706FAD3293F65D500E42796 /* DownloadsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6B1E87F26D5DA9B0062C350 /* DownloadsViewController.swift */; }; @@ -649,7 +648,7 @@ 3706FCBE293F65D500E42796 /* autoconsent-bundle.js in Resources */ = {isa = PBXBuildFile; fileRef = B31055C327A1BA1D001AC618 /* autoconsent-bundle.js */; }; 3706FCBF293F65D500E42796 /* ContentOverlay.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7B1E819C27C8874900FF0E60 /* ContentOverlay.storyboard */; }; 3706FCC0293F65D500E42796 /* FindInPage.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85A0117325AF2EDF00FA6A0C /* FindInPage.storyboard */; }; - 3706FCC1293F65D500E42796 /* HomePage.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85589E7B27BBB8630038AD11 /* HomePage.storyboard */; }; + 3706FCC1293F65D500E42796 /* AddEditFavoriteViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85589E7B27BBB8630038AD11 /* AddEditFavoriteViewController.storyboard */; }; 3706FCC2293F65D500E42796 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = AA80EC8B256C49B8007083E7 /* Localizable.strings */; }; 3706FCC3293F65D500E42796 /* userscript.js in Resources */ = {isa = PBXBuildFile; fileRef = B31055BE27A1BA1D001AC618 /* userscript.js */; }; 3706FCC4293F65D500E42796 /* fb-tds.json in Resources */ = {isa = PBXBuildFile; fileRef = EA4617EF273A28A700F110A2 /* fb-tds.json */; }; @@ -679,7 +678,6 @@ 3706FCE2293F65D500E42796 /* dark-trackers-3.json in Resources */ = {isa = PBXBuildFile; fileRef = AA3439772754D55100B241FA /* dark-trackers-3.json */; }; 3706FCE3293F65D500E42796 /* dark-trackers-2.json in Resources */ = {isa = PBXBuildFile; fileRef = AA3439722754D55100B241FA /* dark-trackers-2.json */; }; 3706FCE4293F65D500E42796 /* Fire.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AAB7320626DD0C37002FACF9 /* Fire.storyboard */; }; - 3706FCE5293F65D500E42796 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85589E8E27BBBBF10038AD11 /* Main.storyboard */; }; 3706FCE6293F65D500E42796 /* social_images in Resources */ = {isa = PBXBuildFile; fileRef = EA18D1C9272F0DC8006DC101 /* social_images */; }; 3706FCE7293F65D500E42796 /* shield-dot-mouse-over.json in Resources */ = {isa = PBXBuildFile; fileRef = AA7EB6E827E880A600036718 /* shield-dot-mouse-over.json */; }; 3706FCE8293F65D500E42796 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = AA80EC91256C49BC007083E7 /* Localizable.stringsdict */; }; @@ -757,7 +755,6 @@ 3706FE14293F661700E42796 /* DownloadListStoreMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B693956026F1C1BC0015B914 /* DownloadListStoreMock.swift */; }; 3706FE15293F661700E42796 /* PrivacyIconViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA91F83827076F1900771A0D /* PrivacyIconViewModelTests.swift */; }; 3706FE16293F661700E42796 /* CSVImporterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B723E0126B0003E00E14D75 /* CSVImporterTests.swift */; }; - 3706FE17293F661700E42796 /* BookmarkStoreMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA652CDA25DDAB32009059CC /* BookmarkStoreMock.swift */; }; 3706FE19293F661700E42796 /* DeviceAuthenticatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBC16A427C488C900E00A38 /* DeviceAuthenticatorTests.swift */; }; 3706FE1A293F661700E42796 /* BrowserProfileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B3F641D27A8D3BD00E0C118 /* BrowserProfileTests.swift */; }; 3706FE1B293F661700E42796 /* PermissionManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6106B9F26A7BE0B0013B453 /* PermissionManagerTests.swift */; }; @@ -1363,7 +1360,6 @@ 4B9579A32AC7AE700062CA31 /* PopUpButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BBE0AA627B9B027003B37A8 /* PopUpButton.swift */; }; 4B9579A42AC7AE700062CA31 /* NetworkProtectionInviteDialog.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B4D606C2A0B29FA00BCD287 /* NetworkProtectionInviteDialog.swift */; }; 4B9579A52AC7AE700062CA31 /* SuggestionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AABEE6A424AA0A7F0043105B /* SuggestionViewController.swift */; }; - 4B9579A72AC7AE700062CA31 /* AddEditFavoriteWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85589E7A27BBB8620038AD11 /* AddEditFavoriteWindow.swift */; }; 4B9579A82AC7AE700062CA31 /* BWKeyStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1D6216B129069BBF00386B2C /* BWKeyStorage.swift */; }; 4B9579A92AC7AE700062CA31 /* VisitViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA7E919E287872EA00AB6B62 /* VisitViewModel.swift */; }; 4B9579AA2AC7AE700062CA31 /* AddressBarTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6676BE02AA986A700525A21 /* AddressBarTextEditor.swift */; }; @@ -1951,7 +1947,7 @@ 4B957BFB2AC7AE700062CA31 /* ContentOverlay.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7B1E819C27C8874900FF0E60 /* ContentOverlay.storyboard */; }; 4B957BFC2AC7AE700062CA31 /* FindInPage.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85A0117325AF2EDF00FA6A0C /* FindInPage.storyboard */; }; 4B957BFD2AC7AE700062CA31 /* JSAlert.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = EEC111E3294D06020086524F /* JSAlert.storyboard */; }; - 4B957BFE2AC7AE700062CA31 /* HomePage.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85589E7B27BBB8630038AD11 /* HomePage.storyboard */; }; + 4B957BFE2AC7AE700062CA31 /* AddEditFavoriteViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85589E7B27BBB8630038AD11 /* AddEditFavoriteViewController.storyboard */; }; 4B957BFF2AC7AE700062CA31 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = AA80EC8B256C49B8007083E7 /* Localizable.strings */; }; 4B957C002AC7AE700062CA31 /* userscript.js in Resources */ = {isa = PBXBuildFile; fileRef = B31055BE27A1BA1D001AC618 /* userscript.js */; }; 4B957C012AC7AE700062CA31 /* fb-tds.json in Resources */ = {isa = PBXBuildFile; fileRef = EA4617EF273A28A700F110A2 /* fb-tds.json */; }; @@ -1981,7 +1977,6 @@ 4B957C1B2AC7AE700062CA31 /* dark-trackers-3.json in Resources */ = {isa = PBXBuildFile; fileRef = AA3439772754D55100B241FA /* dark-trackers-3.json */; }; 4B957C1C2AC7AE700062CA31 /* dark-trackers-2.json in Resources */ = {isa = PBXBuildFile; fileRef = AA3439722754D55100B241FA /* dark-trackers-2.json */; }; 4B957C1D2AC7AE700062CA31 /* Fire.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = AAB7320626DD0C37002FACF9 /* Fire.storyboard */; }; - 4B957C1E2AC7AE700062CA31 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85589E8E27BBBBF10038AD11 /* Main.storyboard */; }; 4B957C1F2AC7AE700062CA31 /* social_images in Resources */ = {isa = PBXBuildFile; fileRef = EA18D1C9272F0DC8006DC101 /* social_images */; }; 4B957C202AC7AE700062CA31 /* shield-dot-mouse-over.json in Resources */ = {isa = PBXBuildFile; fileRef = AA7EB6E827E880A600036718 /* shield-dot-mouse-over.json */; }; 4B957C212AC7AE700062CA31 /* Localizable.stringsdict in Resources */ = {isa = PBXBuildFile; fileRef = AA80EC91256C49BC007083E7 /* Localizable.stringsdict */; }; @@ -2270,13 +2265,11 @@ 85480FCF25D1AA22009424E3 /* ConfigurationStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85480FCE25D1AA22009424E3 /* ConfigurationStore.swift */; }; 854DAAAE2A72B613001E2E24 /* BookmarksBarPromptAssets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 859F30662A72B38500C20372 /* BookmarksBarPromptAssets.xcassets */; }; 85589E7F27BBB8630038AD11 /* AddEditFavoriteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85589E7927BBB8620038AD11 /* AddEditFavoriteViewController.swift */; }; - 85589E8027BBB8630038AD11 /* AddEditFavoriteWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85589E7A27BBB8620038AD11 /* AddEditFavoriteWindow.swift */; }; - 85589E8127BBB8630038AD11 /* HomePage.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85589E7B27BBB8630038AD11 /* HomePage.storyboard */; }; + 85589E8127BBB8630038AD11 /* AddEditFavoriteViewController.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85589E7B27BBB8630038AD11 /* AddEditFavoriteViewController.storyboard */; }; 85589E8227BBB8630038AD11 /* HomePageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85589E7C27BBB8630038AD11 /* HomePageView.swift */; }; 85589E8327BBB8630038AD11 /* HomePageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85589E7D27BBB8630038AD11 /* HomePageViewController.swift */; }; 85589E8727BBB8F20038AD11 /* HomePageFavoritesModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85589E8627BBB8F20038AD11 /* HomePageFavoritesModel.swift */; }; 85589E8D27BBBB870038AD11 /* NavigationBar.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85589E8C27BBBB870038AD11 /* NavigationBar.storyboard */; }; - 85589E8F27BBBBF10038AD11 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 85589E8E27BBBBF10038AD11 /* Main.storyboard */; }; 85589E9127BFB9810038AD11 /* HomePageRecentlyVisitedModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85589E9027BFB9810038AD11 /* HomePageRecentlyVisitedModel.swift */; }; 85589E9427BFE1E70038AD11 /* FavoritesView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85589E9327BFE1E70038AD11 /* FavoritesView.swift */; }; 85589E9A27BFE3C30038AD11 /* FaviconView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 85589E9927BFE3C30038AD11 /* FaviconView.swift */; }; @@ -2446,7 +2439,6 @@ AA652CB125DD825B009059CC /* LocalBookmarkStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA652CB025DD825B009059CC /* LocalBookmarkStoreTests.swift */; }; AA652CCE25DD9071009059CC /* BookmarkListTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA652CCD25DD9071009059CC /* BookmarkListTests.swift */; }; AA652CD325DDA6E9009059CC /* LocalBookmarkManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA652CD225DDA6E9009059CC /* LocalBookmarkManagerTests.swift */; }; - AA652CDB25DDAB32009059CC /* BookmarkStoreMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA652CDA25DDAB32009059CC /* BookmarkStoreMock.swift */; }; AA6820E425502F19005ED0D5 /* WebsiteDataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA6820E325502F19005ED0D5 /* WebsiteDataStore.swift */; }; AA6820EB25503D6A005ED0D5 /* Fire.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA6820EA25503D6A005ED0D5 /* Fire.swift */; }; AA6820F125503DA9005ED0D5 /* FireViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA6820F025503DA9005ED0D5 /* FireViewModel.swift */; }; @@ -2704,6 +2696,9 @@ B64C853826944B880048FEBE /* StoredPermission.swift in Sources */ = {isa = PBXBuildFile; fileRef = B64C853726944B880048FEBE /* StoredPermission.swift */; }; B64C853D26944B940048FEBE /* PermissionStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B64C853C26944B940048FEBE /* PermissionStore.swift */; }; B64C85422694590B0048FEBE /* PermissionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B64C85412694590B0048FEBE /* PermissionButton.swift */; }; + B65211252B29A42C00B30633 /* BookmarkStoreMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA652CDA25DDAB32009059CC /* BookmarkStoreMock.swift */; }; + B65211262B29A42E00B30633 /* BookmarkStoreMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA652CDA25DDAB32009059CC /* BookmarkStoreMock.swift */; }; + B65211272B29A43000B30633 /* BookmarkStoreMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = AA652CDA25DDAB32009059CC /* BookmarkStoreMock.swift */; }; B65349AA265CF45000DCC645 /* DispatchQueueExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B65349A9265CF45000DCC645 /* DispatchQueueExtensionsTests.swift */; }; B655124829A79465009BFE1C /* NavigationActionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B66B9C5B29A5EBAD0010E8F3 /* NavigationActionExtension.swift */; }; B655124929A79465009BFE1C /* NavigationActionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B66B9C5B29A5EBAD0010E8F3 /* NavigationActionExtension.swift */; }; @@ -2733,6 +2728,7 @@ B662D3D92755D7AD0035D4D6 /* PixelStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B662D3D82755D7AD0035D4D6 /* PixelStoreTests.swift */; }; B662D3DE275613BB0035D4D6 /* EncryptionKeyStoreMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B662D3DD275613BB0035D4D6 /* EncryptionKeyStoreMock.swift */; }; B662D3DF275616FF0035D4D6 /* EncryptionKeyStoreMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B662D3DD275613BB0035D4D6 /* EncryptionKeyStoreMock.swift */; }; + B6656E122B29E3BE008798A1 /* DownloadListStoreMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B693956026F1C1BC0015B914 /* DownloadListStoreMock.swift */; }; B6676BE12AA986A700525A21 /* AddressBarTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6676BE02AA986A700525A21 /* AddressBarTextEditor.swift */; }; B6676BE22AA986A700525A21 /* AddressBarTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6676BE02AA986A700525A21 /* AddressBarTextEditor.swift */; }; B6685E3D29A602D90043D2EE /* ExternalAppSchemeHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = B687B7CB2947A1E9001DEA6F /* ExternalAppSchemeHandler.swift */; }; @@ -2790,7 +2786,6 @@ B693955726F04BEC0015B914 /* MouseOverButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B693954926F04BEB0015B914 /* MouseOverButton.swift */; }; B693955D26F19CD70015B914 /* DownloadListStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B693955C26F19CD70015B914 /* DownloadListStoreTests.swift */; }; B693955F26F1C17F0015B914 /* DownloadListCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B693955E26F1C17F0015B914 /* DownloadListCoordinatorTests.swift */; }; - B693956126F1C1BC0015B914 /* DownloadListStoreMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B693956026F1C1BC0015B914 /* DownloadListStoreMock.swift */; }; B693956326F1C2A40015B914 /* FileDownloadManagerMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B693956226F1C2A40015B914 /* FileDownloadManagerMock.swift */; }; B693956926F352DB0015B914 /* DownloadsWebViewMock.m in Sources */ = {isa = PBXBuildFile; fileRef = B693956826F352DB0015B914 /* DownloadsWebViewMock.m */; }; B696AFFB2AC5924800C93203 /* FileLineError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B696AFFA2AC5924800C93203 /* FileLineError.swift */; }; @@ -2846,6 +2841,9 @@ B6B1E88B26D774090062C350 /* LinkButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6B1E88A26D774090062C350 /* LinkButton.swift */; }; B6B2400E28083B49001B8F3A /* WebViewContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6B2400D28083B49001B8F3A /* WebViewContainerView.swift */; }; B6B3E0E12657EA7A0040E0A2 /* NSScreenExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6B3E0DC2657E9CF0040E0A2 /* NSScreenExtension.swift */; }; + B6B71C582B23379600487131 /* NSLayoutConstraintExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6B71C572B23379600487131 /* NSLayoutConstraintExtension.swift */; }; + B6B71C592B23379600487131 /* NSLayoutConstraintExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6B71C572B23379600487131 /* NSLayoutConstraintExtension.swift */; }; + B6B71C5A2B23379600487131 /* NSLayoutConstraintExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6B71C572B23379600487131 /* NSLayoutConstraintExtension.swift */; }; B6B77BE8297973D4001E68A1 /* Navigation in Frameworks */ = {isa = PBXBuildFile; productRef = B6B77BE7297973D4001E68A1 /* Navigation */; }; B6BBF1702744CDE1004F850E /* CoreDataStoreTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6BBF16F2744CDE1004F850E /* CoreDataStoreTests.swift */; }; B6BBF1722744CE36004F850E /* FireproofDomainsStoreMock.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6BBF1712744CE36004F850E /* FireproofDomainsStoreMock.swift */; }; @@ -3645,13 +3643,11 @@ 85480FCE25D1AA22009424E3 /* ConfigurationStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigurationStore.swift; sourceTree = ""; }; 8553FF51257523760029327F /* URLSuggestedFilenameTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSuggestedFilenameTests.swift; sourceTree = ""; }; 85589E7927BBB8620038AD11 /* AddEditFavoriteViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddEditFavoriteViewController.swift; sourceTree = ""; }; - 85589E7A27BBB8620038AD11 /* AddEditFavoriteWindow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddEditFavoriteWindow.swift; sourceTree = ""; }; - 85589E7B27BBB8630038AD11 /* HomePage.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = HomePage.storyboard; sourceTree = ""; }; + 85589E7B27BBB8630038AD11 /* AddEditFavoriteViewController.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = AddEditFavoriteViewController.storyboard; sourceTree = ""; }; 85589E7C27BBB8630038AD11 /* HomePageView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomePageView.swift; sourceTree = ""; }; 85589E7D27BBB8630038AD11 /* HomePageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomePageViewController.swift; sourceTree = ""; }; 85589E8627BBB8F20038AD11 /* HomePageFavoritesModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomePageFavoritesModel.swift; sourceTree = ""; }; 85589E8C27BBBB870038AD11 /* NavigationBar.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = NavigationBar.storyboard; sourceTree = ""; }; - 85589E8E27BBBBF10038AD11 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; 85589E9027BFB9810038AD11 /* HomePageRecentlyVisitedModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePageRecentlyVisitedModel.swift; sourceTree = ""; }; 85589E9227BFBBD60038AD11 /* History 4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "History 4.xcdatamodel"; sourceTree = ""; }; 85589E9327BFE1E70038AD11 /* FavoritesView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritesView.swift; sourceTree = ""; }; @@ -4125,6 +4121,7 @@ B6B1E88A26D774090062C350 /* LinkButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LinkButton.swift; sourceTree = ""; }; B6B2400D28083B49001B8F3A /* WebViewContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewContainerView.swift; sourceTree = ""; }; B6B3E0DC2657E9CF0040E0A2 /* NSScreenExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSScreenExtension.swift; sourceTree = ""; }; + B6B71C572B23379600487131 /* NSLayoutConstraintExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSLayoutConstraintExtension.swift; sourceTree = ""; }; B6BBF16F2744CDE1004F850E /* CoreDataStoreTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataStoreTests.swift; sourceTree = ""; }; B6BBF1712744CE36004F850E /* FireproofDomainsStoreMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FireproofDomainsStoreMock.swift; sourceTree = ""; }; B6BBF17327475B15004F850E /* PopupBlockedPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupBlockedPopover.swift; sourceTree = ""; }; @@ -6489,7 +6486,6 @@ AA585DB02490E6FA00E9A3E2 /* MainWindow */ = { isa = PBXGroup; children = ( - 85589E8E27BBBBF10038AD11 /* Main.storyboard */, AA7412BC24D2BEEE00D22FE0 /* MainWindow.swift */, AA7412B424D1536B00D22FE0 /* MainWindowController.swift */, AA585DAE2490E6E600E9A3E2 /* MainViewController.swift */, @@ -6586,7 +6582,6 @@ children = ( AA652CB025DD825B009059CC /* LocalBookmarkStoreTests.swift */, 986189E52A7CFB3E001B4519 /* LocalBookmarkStoreSavingTests.swift */, - AA652CDA25DDAB32009059CC /* BookmarkStoreMock.swift */, ); path = Services; sourceTree = ""; @@ -7081,6 +7076,7 @@ isa = PBXGroup; children = ( 987799F52999996B005D8EB6 /* BookmarkDatabase.swift */, + AA652CDA25DDAB32009059CC /* BookmarkStoreMock.swift */, 4B9292DA2667125D00AD2C21 /* ContextualMenu.swift */, B6DA06E52913F39400225DE2 /* MenuItemSelectors.swift */, AAC5E4D625D6A710007F5990 /* BookmarkStore.swift */, @@ -7211,6 +7207,7 @@ B657841925FA484B00D8DB33 /* NSException+Catch.m */, B657841E25FA497600D8DB33 /* NSException+Catch.swift */, 4B139AFC26B60BD800894F82 /* NSImageExtensions.swift */, + B6B71C572B23379600487131 /* NSLayoutConstraintExtension.swift */, AA6EF9B2250785D5004754E6 /* NSMenuExtension.swift */, AA72D5FD25FFF94E00C77619 /* NSMenuItemExtension.swift */, 4B980E202817604000282EE1 /* NSNotificationName+Debug.swift */, @@ -7278,12 +7275,11 @@ AAE71DB325F66A3F00D74437 /* View */ = { isa = PBXGroup; children = ( + 85589E7B27BBB8630038AD11 /* AddEditFavoriteViewController.storyboard */, 85589E7927BBB8620038AD11 /* AddEditFavoriteViewController.swift */, - 85589E7A27BBB8620038AD11 /* AddEditFavoriteWindow.swift */, 85589E9D27BFE4500038AD11 /* DefaultBrowserPromptView.swift */, 85589E9327BFE1E70038AD11 /* FavoritesView.swift */, 56D6A3D529DB2BAB0055215A /* ContinueSetUpView.swift */, - 85589E7B27BBB8630038AD11 /* HomePage.storyboard */, 85AC7AD827BD625000FFB69B /* HomePageAssets.xcassets */, 85589E7C27BBB8630038AD11 /* HomePageView.swift */, 1DCFBC8929ADF32B00313531 /* BurnerHomePageView.swift */, @@ -8422,7 +8418,7 @@ 3706FCBF293F65D500E42796 /* ContentOverlay.storyboard in Resources */, 3706FCC0293F65D500E42796 /* FindInPage.storyboard in Resources */, EEC8EB3F2982CA440065AA39 /* JSAlert.storyboard in Resources */, - 3706FCC1293F65D500E42796 /* HomePage.storyboard in Resources */, + 3706FCC1293F65D500E42796 /* AddEditFavoriteViewController.storyboard in Resources */, 3706FCC2293F65D500E42796 /* Localizable.strings in Resources */, 3706FCC3293F65D500E42796 /* userscript.js in Resources */, 3706FCC4293F65D500E42796 /* fb-tds.json in Resources */, @@ -8452,7 +8448,6 @@ 3706FCE2293F65D500E42796 /* dark-trackers-3.json in Resources */, 3706FCE3293F65D500E42796 /* dark-trackers-2.json in Resources */, 3706FCE4293F65D500E42796 /* Fire.storyboard in Resources */, - 3706FCE5293F65D500E42796 /* Main.storyboard in Resources */, 3706FCE6293F65D500E42796 /* social_images in Resources */, 3706FCE7293F65D500E42796 /* shield-dot-mouse-over.json in Resources */, 3706FCE8293F65D500E42796 /* Localizable.stringsdict in Resources */, @@ -8549,7 +8544,7 @@ 4B957BFB2AC7AE700062CA31 /* ContentOverlay.storyboard in Resources */, 4B957BFC2AC7AE700062CA31 /* FindInPage.storyboard in Resources */, 4B957BFD2AC7AE700062CA31 /* JSAlert.storyboard in Resources */, - 4B957BFE2AC7AE700062CA31 /* HomePage.storyboard in Resources */, + 4B957BFE2AC7AE700062CA31 /* AddEditFavoriteViewController.storyboard in Resources */, 4B957BFF2AC7AE700062CA31 /* Localizable.strings in Resources */, 4B957C002AC7AE700062CA31 /* userscript.js in Resources */, 4B957C012AC7AE700062CA31 /* fb-tds.json in Resources */, @@ -8579,7 +8574,6 @@ 4B957C1B2AC7AE700062CA31 /* dark-trackers-3.json in Resources */, 4B957C1C2AC7AE700062CA31 /* dark-trackers-2.json in Resources */, 4B957C1D2AC7AE700062CA31 /* Fire.storyboard in Resources */, - 4B957C1E2AC7AE700062CA31 /* Main.storyboard in Resources */, 4B957C1F2AC7AE700062CA31 /* social_images in Resources */, 4B957C202AC7AE700062CA31 /* shield-dot-mouse-over.json in Resources */, 4B957C212AC7AE700062CA31 /* Localizable.stringsdict in Resources */, @@ -8646,7 +8640,7 @@ 7B1E819F27C8874900FF0E60 /* ContentOverlay.storyboard in Resources */, 85A0117425AF2EDF00FA6A0C /* FindInPage.storyboard in Resources */, EEC111E4294D06020086524F /* JSAlert.storyboard in Resources */, - 85589E8127BBB8630038AD11 /* HomePage.storyboard in Resources */, + 85589E8127BBB8630038AD11 /* AddEditFavoriteViewController.storyboard in Resources */, AA80EC89256C49B8007083E7 /* Localizable.strings in Resources */, B31055C627A1BA1D001AC618 /* userscript.js in Resources */, EA4617F0273A28A700F110A2 /* fb-tds.json in Resources */, @@ -8676,7 +8670,6 @@ AA34397D2754D55100B241FA /* dark-trackers-3.json in Resources */, AA3439782754D55100B241FA /* dark-trackers-2.json in Resources */, AAB7320726DD0C37002FACF9 /* Fire.storyboard in Resources */, - 85589E8F27BBBBF10038AD11 /* Main.storyboard in Resources */, EA18D1CA272F0DC8006DC101 /* social_images in Resources */, AA7EB6E927E880A600036718 /* shield-dot-mouse-over.json in Resources */, AA80EC8F256C49BC007083E7 /* Localizable.stringsdict in Resources */, @@ -9152,7 +9145,6 @@ 3706FACC293F65D500E42796 /* SaveCredentialsViewController.swift in Sources */, 3706FACD293F65D500E42796 /* PopUpButton.swift in Sources */, 3706FACE293F65D500E42796 /* SuggestionViewController.swift in Sources */, - 3706FACF293F65D500E42796 /* AddEditFavoriteWindow.swift in Sources */, 7BFE95552A9DF2990081ABE9 /* UserDefaults+NetworkProtectionWaitlist.swift in Sources */, 3706FAD1293F65D500E42796 /* VisitViewModel.swift in Sources */, 3706FAD2293F65D500E42796 /* Atb.swift in Sources */, @@ -9195,6 +9187,7 @@ 3706FEBC293F6EFF00E42796 /* BWResponse.swift in Sources */, 3706FAF4293F65D500E42796 /* SafariBookmarksReader.swift in Sources */, 31C9ADE62AF0564500CEF57D /* WaitlistFeatureSetupHandler.swift in Sources */, + B65211262B29A42E00B30633 /* BookmarkStoreMock.swift in Sources */, 3706FAF5293F65D500E42796 /* SafariVersionReader.swift in Sources */, 3706FAF6293F65D500E42796 /* LoginFaviconView.swift in Sources */, 3706FEC0293F6EFF00E42796 /* BWRequest.swift in Sources */, @@ -9296,6 +9289,7 @@ 3706FB48293F65D500E42796 /* AutofillPreferencesModel.swift in Sources */, 3168506E2AF3AD1D009A2828 /* WaitlistViewControllerPresenter.swift in Sources */, 3706FB49293F65D500E42796 /* NSException+Catch.swift in Sources */, + B6B71C592B23379600487131 /* NSLayoutConstraintExtension.swift in Sources */, 3706FB4A293F65D500E42796 /* PasswordManagementNoteModel.swift in Sources */, 3706FB4B293F65D500E42796 /* CookieNotificationAnimationModel.swift in Sources */, 3706FB4C293F65D500E42796 /* SharingMenu.swift in Sources */, @@ -9804,7 +9798,6 @@ 3706FE15293F661700E42796 /* PrivacyIconViewModelTests.swift in Sources */, 56D145F229E6F06D00E3488A /* MockBookmarkManager.swift in Sources */, 3706FE16293F661700E42796 /* CSVImporterTests.swift in Sources */, - 3706FE17293F661700E42796 /* BookmarkStoreMock.swift in Sources */, 56B234C02A84EFD800F2A1CC /* NavigationBarUrlExtensionsTests.swift in Sources */, 3706FE19293F661700E42796 /* DeviceAuthenticatorTests.swift in Sources */, 3706FE1A293F661700E42796 /* BrowserProfileTests.swift in Sources */, @@ -10245,7 +10238,6 @@ 4B9579A32AC7AE700062CA31 /* PopUpButton.swift in Sources */, 4B9579A42AC7AE700062CA31 /* NetworkProtectionInviteDialog.swift in Sources */, 4B9579A52AC7AE700062CA31 /* SuggestionViewController.swift in Sources */, - 4B9579A72AC7AE700062CA31 /* AddEditFavoriteWindow.swift in Sources */, 4B9579A82AC7AE700062CA31 /* BWKeyStorage.swift in Sources */, 4B9579A92AC7AE700062CA31 /* VisitViewModel.swift in Sources */, 4B9579AA2AC7AE700062CA31 /* AddressBarTextEditor.swift in Sources */, @@ -10742,6 +10734,8 @@ 4B957B802AC7AE700062CA31 /* NSAlertExtension.swift in Sources */, 4B957B812AC7AE700062CA31 /* ThirdPartyBrowser.swift in Sources */, 4B957B822AC7AE700062CA31 /* SearchNonexistentDomainNavigationResponder.swift in Sources */, + B6B71C5A2B23379600487131 /* NSLayoutConstraintExtension.swift in Sources */, + B65211272B29A43000B30633 /* BookmarkStoreMock.swift in Sources */, 4B957B832AC7AE700062CA31 /* CircularProgressView.swift in Sources */, 4B957B842AC7AE700062CA31 /* SuggestionContainer.swift in Sources */, 4B957B852AC7AE700062CA31 /* FindInPageTabExtension.swift in Sources */, @@ -10963,6 +10957,7 @@ 4BBDEE9428FC14760092FAA6 /* ConnectBitwardenViewController.swift in Sources */, 1DDF076428F815AD00EDFBE3 /* BWManager.swift in Sources */, 9833912F27AAA3CE00DAF119 /* AppTrackerDataSetProvider.swift in Sources */, + B65211252B29A42C00B30633 /* BookmarkStoreMock.swift in Sources */, 4BA1A6B3258B080A00F6F690 /* EncryptionKeyGeneration.swift in Sources */, 37B11B3928095E6600CBB621 /* TabLazyLoader.swift in Sources */, 4B9DB03B2A983B24000927DB /* InvitedToWaitlistView.swift in Sources */, @@ -10971,7 +10966,6 @@ 4BBE0AA727B9B027003B37A8 /* PopUpButton.swift in Sources */, 4B4D60CF2A0C849600BCD287 /* NetworkProtectionInviteDialog.swift in Sources */, AABEE6A524AA0A7F0043105B /* SuggestionViewController.swift in Sources */, - 85589E8027BBB8630038AD11 /* AddEditFavoriteWindow.swift in Sources */, 1D6216B229069BBF00386B2C /* BWKeyStorage.swift in Sources */, AA7E919F287872EA00AB6B62 /* VisitViewModel.swift in Sources */, B6676BE12AA986A700525A21 /* AddressBarTextEditor.swift in Sources */, @@ -11148,6 +11142,7 @@ B6DB3CF926A00E2D00D459B7 /* AVCaptureDevice+SwizzledAuthState.swift in Sources */, 1E0C72062ABC63BD00802009 /* SubscriptionPagesUserScript.swift in Sources */, AAAB9116288EB46B00A057A9 /* VisitMenuItem.swift in Sources */, + B6B71C582B23379600487131 /* NSLayoutConstraintExtension.swift in Sources */, 4BA1A6BD258B082300F6F690 /* EncryptionKeyStore.swift in Sources */, B6C00ED7292FB4B4009C73A6 /* TabExtensionsBuilder.swift in Sources */, 4BE65474271FCD40008D1D63 /* PasswordManagementIdentityItemView.swift in Sources */, @@ -11591,6 +11586,7 @@ B662D3DE275613BB0035D4D6 /* EncryptionKeyStoreMock.swift in Sources */, 1D3B1ABF29369FC8006F4388 /* BWEncryptionTests.swift in Sources */, B6F56567299A414300A04298 /* WKWebViewMockingExtension.swift in Sources */, + B6656E122B29E3BE008798A1 /* DownloadListStoreMock.swift in Sources */, 37D23780287EFEE200BCE03B /* PinnedTabsManagerTests.swift in Sources */, AA0877BA26D5161D00B05660 /* WebKitVersionProviderTests.swift in Sources */, B69B50462726C5C200758A2B /* AtbAndVariantCleanupTests.swift in Sources */, @@ -11636,10 +11632,8 @@ B630793526731BC400DCEE41 /* URLSuggestedFilenameTests.swift in Sources */, B603974E29C1F93600902A34 /* TabPermissionsTests.swift in Sources */, 85AC3B4925DAC9BD00C7D2AA /* ConfigurationStorageTests.swift in Sources */, - B693956126F1C1BC0015B914 /* DownloadListStoreMock.swift in Sources */, AA91F83927076F1900771A0D /* PrivacyIconViewModelTests.swift in Sources */, 4B723E0726B0003E00E14D75 /* CSVImporterTests.swift in Sources */, - AA652CDB25DDAB32009059CC /* BookmarkStoreMock.swift in Sources */, 4BCF15EC2ABB9AF80083F6DF /* NetworkProtectionRemoteMessageTests.swift in Sources */, B62EB47C25BAD3BB005745C6 /* WKWebViewPrivateMethodsAvailabilityTests.swift in Sources */, 4BBC16A527C488C900E00A38 /* DeviceAuthenticatorTests.swift in Sources */, diff --git a/DuckDuckGo/Bookmarks/Model/BookmarkManager.swift b/DuckDuckGo/Bookmarks/Model/BookmarkManager.swift index e97f87e19e..a432fcb295 100644 --- a/DuckDuckGo/Bookmarks/Model/BookmarkManager.swift +++ b/DuckDuckGo/Bookmarks/Model/BookmarkManager.swift @@ -57,13 +57,13 @@ final class LocalBookmarkManager: BookmarkManager { static let shared = LocalBookmarkManager() - private init() { - self.subscribeToFavoritesDisplayMode() - } - - init(bookmarkStore: BookmarkStore, faviconManagement: FaviconManagement) { - self.bookmarkStore = bookmarkStore - self.faviconManagement = faviconManagement + init(bookmarkStore: BookmarkStore? = nil, faviconManagement: FaviconManagement? = nil) { + if let bookmarkStore { + self.bookmarkStore = bookmarkStore + } + if let faviconManagement { + self.faviconManagement = faviconManagement + } self.subscribeToFavoritesDisplayMode() } diff --git a/UnitTests/Bookmarks/Services/BookmarkStoreMock.swift b/DuckDuckGo/Bookmarks/Services/BookmarkStoreMock.swift similarity index 68% rename from UnitTests/Bookmarks/Services/BookmarkStoreMock.swift rename to DuckDuckGo/Bookmarks/Services/BookmarkStoreMock.swift index 7e5265b12e..b6ea21c7e7 100644 --- a/UnitTests/Bookmarks/Services/BookmarkStoreMock.swift +++ b/DuckDuckGo/Bookmarks/Services/BookmarkStoreMock.swift @@ -16,13 +16,35 @@ // limitations under the License. // +#if DEBUG + import Bookmarks import Foundation -import XCTest -@testable import DuckDuckGo_Privacy_Browser - -final class BookmarkStoreMock: BookmarkStore { +public final class BookmarkStoreMock: BookmarkStore { + + init(loadAllCalled: Bool = false, bookmarks: [BaseBookmarkEntity]? = nil, loadError: Error? = nil, saveBookmarkCalled: Bool = false, saveBookmarkSuccess: Bool = true, saveBookmarkError: Error? = nil, saveFolderCalled: Bool = false, saveFolderSuccess: Bool = true, saveFolderError: Error? = nil, removeCalled: Bool = false, removeSuccess: Bool = true, removeError: Error? = nil, updateBookmarkCalled: Bool = false, updateFolderCalled: Bool = false, addChildCalled: Bool = false, updateObjectsCalled: Bool = false, importBookmarksCalled: Bool = false, canMoveObjectWithUUIDCalled: Bool = false, moveObjectUUIDCalled: Bool = false, updateFavoriteIndexCalled: Bool = false) { + self.loadAllCalled = loadAllCalled + self.bookmarks = bookmarks + self.loadError = loadError + self.saveBookmarkCalled = saveBookmarkCalled + self.saveBookmarkSuccess = saveBookmarkSuccess + self.saveBookmarkError = saveBookmarkError + self.saveFolderCalled = saveFolderCalled + self.saveFolderSuccess = saveFolderSuccess + self.saveFolderError = saveFolderError + self.removeCalled = removeCalled + self.removeSuccess = removeSuccess + self.removeError = removeError + self.updateBookmarkCalled = updateBookmarkCalled + self.updateFolderCalled = updateFolderCalled + self.addChildCalled = addChildCalled + self.updateObjectsCalled = updateObjectsCalled + self.importBookmarksCalled = importBookmarksCalled + self.canMoveObjectWithUUIDCalled = canMoveObjectWithUUIDCalled + self.moveObjectUUIDCalled = moveObjectUUIDCalled + self.updateFavoriteIndexCalled = updateFavoriteIndexCalled + } var loadAllCalled = false var bookmarks: [BaseBookmarkEntity]? @@ -112,3 +134,5 @@ final class BookmarkStoreMock: BookmarkStore { func applyFavoritesDisplayMode(_ configuration: FavoritesDisplayMode) {} func handleFavoritesAfterDisablingSync() {} } + +#endif diff --git a/DuckDuckGo/BookmarksBar/View/BookmarksBarViewController.swift b/DuckDuckGo/BookmarksBar/View/BookmarksBarViewController.swift index 48e990e51d..1a59411f58 100644 --- a/DuckDuckGo/BookmarksBar/View/BookmarksBarViewController.swift +++ b/DuckDuckGo/BookmarksBar/View/BookmarksBarViewController.swift @@ -41,15 +41,21 @@ final class BookmarksBarViewController: NSViewController { @UserDefaultsWrapper(key: .bookmarksBarPromptShown, defaultValue: false) var bookmarksBarPromptShown: Bool - init?(coder: NSCoder, tabCollectionViewModel: TabCollectionViewModel) { + static func create(tabCollectionViewModel: TabCollectionViewModel, bookmarkManager: BookmarkManager = LocalBookmarkManager.shared) -> BookmarksBarViewController { + NSStoryboard(name: "BookmarksBar", bundle: nil).instantiateInitialController { coder in + self.init(coder: coder, tabCollectionViewModel: tabCollectionViewModel, bookmarkManager: bookmarkManager) + }! + } + + init?(coder: NSCoder, tabCollectionViewModel: TabCollectionViewModel, bookmarkManager: BookmarkManager) { self.tabCollectionViewModel = tabCollectionViewModel - self.viewModel = BookmarksBarViewModel(bookmarkManager: LocalBookmarkManager.shared, tabCollectionViewModel: tabCollectionViewModel) + self.viewModel = BookmarksBarViewModel(bookmarkManager: bookmarkManager, tabCollectionViewModel: tabCollectionViewModel) super.init(coder: coder) } required init?(coder: NSCoder) { - fatalError("TabBarViewController: Bad initializer") + fatalError("BookmarksBarViewController: Bad initializer") } // MARK: - View Lifecycle diff --git a/DuckDuckGo/Common/Database/Database.swift b/DuckDuckGo/Common/Database/Database.swift index 7af2ce4cf2..574a079ee9 100644 --- a/DuckDuckGo/Common/Database/Database.swift +++ b/DuckDuckGo/Common/Database/Database.swift @@ -58,7 +58,9 @@ final class Database { model: .init(byMerging: [mainModel, httpsUpgradeModel])!), nil) } #if DEBUG - assert(![.unitTests, .xcPreviews].contains(NSApp.runType), "Use CoreData.---Container() methods for testing purposes") + assert(![.unitTests, .xcPreviews].contains(NSApp.runType), { + "Use CoreData.---Container() methods for testing purposes:\n" + Thread.callStackSymbols.description + }()) #endif let keyStore: EncryptionKeyStoring diff --git a/DuckDuckGo/HomePage/View/AddEditFavoriteWindow.swift b/DuckDuckGo/Common/Extensions/NSLayoutConstraintExtension.swift similarity index 53% rename from DuckDuckGo/HomePage/View/AddEditFavoriteWindow.swift rename to DuckDuckGo/Common/Extensions/NSLayoutConstraintExtension.swift index c153324937..9bd5ba66aa 100644 --- a/DuckDuckGo/HomePage/View/AddEditFavoriteWindow.swift +++ b/DuckDuckGo/Common/Extensions/NSLayoutConstraintExtension.swift @@ -1,7 +1,7 @@ // -// AddEditFavoriteWindow.swift +// NSLayoutConstraintExtension.swift // -// Copyright © 2021 DuckDuckGo. All rights reserved. +// 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. @@ -16,21 +16,14 @@ // limitations under the License. // -import Cocoa +import AppKit +import Foundation -final class AddEditFavoriteWindow: NSWindow { +extension NSLayoutConstraint { - enum Size { - static let width: CGFloat = 450 - static let height: CGFloat = 175 + func priority(_ priority: Float) -> Self { + self.priority = .init(priority) + return self } - override var canBecomeMain: Bool { false } - - // swiftlint:disable force_cast - var addEditFavoriteViewController: AddEditFavoriteViewController { - contentViewController as! AddEditFavoriteViewController - } - // swiftlint:enable force_cast - } diff --git a/DuckDuckGo/Common/Extensions/NSScreenExtension.swift b/DuckDuckGo/Common/Extensions/NSScreenExtension.swift index d8ecfd3735..b740fe6faa 100644 --- a/DuckDuckGo/Common/Extensions/NSScreenExtension.swift +++ b/DuckDuckGo/Common/Extensions/NSScreenExtension.swift @@ -20,6 +20,8 @@ import AppKit extension NSScreen { + static let fallbackHeadlessScreenFrame = NSRect(x: 0, y: 100, width: 1280, height: 900) + static var dockScreen: NSScreen? { screens.min(by: { ($0.frame.height - $0.visibleFrame.height) > ($1.frame.height - $1.visibleFrame.height) }) } diff --git a/DuckDuckGo/Common/Extensions/NSViewControllerExtension.swift b/DuckDuckGo/Common/Extensions/NSViewControllerExtension.swift index 3ca98220b8..5d080f5bfb 100644 --- a/DuckDuckGo/Common/Extensions/NSViewControllerExtension.swift +++ b/DuckDuckGo/Common/Extensions/NSViewControllerExtension.swift @@ -79,9 +79,11 @@ extension NSViewController { } } - func addAndLayoutChild(_ vc: NSViewController) { + func addAndLayoutChild(_ vc: NSViewController, into containerView: NSView? = nil) { + assert(containerView == nil || sequence(first: containerView!, next: { $0.superview }).contains(self.view), + "\(containerView!) is not a part of \(self) view hierarchy") self.addChild(vc) - view.addAndLayout(vc.view) + (containerView ?? self.view).addAndLayout(vc.view) } func removeCompletely() { diff --git a/DuckDuckGo/Common/Extensions/NSViewExtension.swift b/DuckDuckGo/Common/Extensions/NSViewExtension.swift index 3e1526f586..0d8bbc4f59 100644 --- a/DuckDuckGo/Common/Extensions/NSViewExtension.swift +++ b/DuckDuckGo/Common/Extensions/NSViewExtension.swift @@ -47,11 +47,13 @@ extension NSView { layer?.cornerRadius = radius } - func addAndLayout(_ subView: NSView) { - subView.frame = bounds - subView.autoresizingMask = [.height, .width] - subView.translatesAutoresizingMaskIntoConstraints = true - addSubview(subView) + func addAndLayout(_ subview: NSView) { + subview.translatesAutoresizingMaskIntoConstraints = false + subview.frame = bounds + subview.autoresizingMask = [.height, .width] + subview.translatesAutoresizingMaskIntoConstraints = true + + addSubview(subview) } func wrappedInContainer(padding: CGFloat = 0) -> NSView { @@ -72,6 +74,11 @@ extension NSView { return containerView } + func hidden() -> Self { + self.isHidden = true + return self + } + func makeMeFirstResponder() { guard let window = window else { os_log("%s: Window not available", type: .error, className) diff --git a/DuckDuckGo/Common/View/AppKit/ColorView.swift b/DuckDuckGo/Common/View/AppKit/ColorView.swift index 460435aa48..07e5a383f5 100644 --- a/DuckDuckGo/Common/View/AppKit/ColorView.swift +++ b/DuckDuckGo/Common/View/AppKit/ColorView.swift @@ -26,8 +26,14 @@ internal class ColorView: NSView { setupView() } - override init(frame frameRect: NSRect) { - super.init(frame: frameRect) + init(frame: NSRect, backgroundColor: NSColor? = nil, cornerRadius: CGFloat = 0, borderColor: NSColor? = nil, borderWidth: CGFloat = 0, interceptClickEvents: Bool = false) { + super.init(frame: frame) + + self.backgroundColor = backgroundColor + self.cornerRadius = cornerRadius + self.borderColor = borderColor + self.borderWidth = borderWidth + self.interceptClickEvents = interceptClickEvents setupView() } diff --git a/DuckDuckGo/FileDownload/Services/DownloadListStore.swift b/DuckDuckGo/FileDownload/Services/DownloadListStore.swift index a60415329f..98d890e4a7 100644 --- a/DuckDuckGo/FileDownload/Services/DownloadListStore.swift +++ b/DuckDuckGo/FileDownload/Services/DownloadListStore.swift @@ -55,7 +55,7 @@ final class DownloadListStore: DownloadListStoring { private var context: NSManagedObjectContext? { if case .none = _context { #if DEBUG - if case .unitTests = NSApp.runType { + if [.unitTests, .xcPreviews].contains(NSApp.runType) { _context = .some(.none) return .none } diff --git a/DuckDuckGo/FindInPage/FindInPageViewController.swift b/DuckDuckGo/FindInPage/FindInPageViewController.swift index b3db6b9457..c761ac0fc3 100644 --- a/DuckDuckGo/FindInPage/FindInPageViewController.swift +++ b/DuckDuckGo/FindInPage/FindInPageViewController.swift @@ -43,6 +43,10 @@ final class FindInPageViewController: NSViewController { private var modelCancellables = Set() + static func create() -> FindInPageViewController { + (NSStoryboard(name: "FindInPage", bundle: nil).instantiateInitialController() as? FindInPageViewController)! + } + override func viewDidLoad() { super.viewDidLoad() focusRingView.strokedBackgroundColor = NSColor.findInPageFocusedBackgroundColor diff --git a/DuckDuckGo/Fire/View/FireViewController.swift b/DuckDuckGo/Fire/View/FireViewController.swift index 730b483f26..919fc8bd71 100644 --- a/DuckDuckGo/Fire/View/FireViewController.swift +++ b/DuckDuckGo/Fire/View/FireViewController.swift @@ -43,13 +43,17 @@ final class FireViewController: NSViewController { private var fireAnimationView: AnimationView? private var fireAnimationViewLoadingTask: Task<(), Never>? + static func create(tabCollectionViewModel: TabCollectionViewModel, fireViewModel: FireViewModel? = nil) -> FireViewController { + NSStoryboard(name: "Fire", bundle: nil).instantiateInitialController { coder in + self.init(coder: coder, tabCollectionViewModel: tabCollectionViewModel, fireViewModel: fireViewModel) + }! + } + required init?(coder: NSCoder) { fatalError("TabBarViewController: Bad initializer") } - init?(coder: NSCoder, - tabCollectionViewModel: TabCollectionViewModel, - fireViewModel: FireViewModel? = nil) { + init?(coder: NSCoder, tabCollectionViewModel: TabCollectionViewModel, fireViewModel: FireViewModel? = nil) { self.tabCollectionViewModel = tabCollectionViewModel self.fireViewModel = fireViewModel ?? FireCoordinator.fireViewModel diff --git a/DuckDuckGo/HomePage/View/HomePage.storyboard b/DuckDuckGo/HomePage/View/AddEditFavoriteViewController.storyboard similarity index 71% rename from DuckDuckGo/HomePage/View/HomePage.storyboard rename to DuckDuckGo/HomePage/View/AddEditFavoriteViewController.storyboard index fec70fd10a..45003a47b6 100644 --- a/DuckDuckGo/HomePage/View/HomePage.storyboard +++ b/DuckDuckGo/HomePage/View/AddEditFavoriteViewController.storyboard @@ -1,54 +1,10 @@ - + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -57,7 +13,7 @@ - + @@ -77,7 +33,7 @@ - + @@ -87,7 +43,7 @@ - + @@ -97,7 +53,7 @@ - + @@ -107,7 +63,7 @@ - + @@ -166,12 +122,7 @@ Gw - + - - - - - diff --git a/DuckDuckGo/HomePage/View/AddEditFavoriteViewController.swift b/DuckDuckGo/HomePage/View/AddEditFavoriteViewController.swift index 811df1e6ab..3be8132f39 100644 --- a/DuckDuckGo/HomePage/View/AddEditFavoriteViewController.swift +++ b/DuckDuckGo/HomePage/View/AddEditFavoriteViewController.swift @@ -31,6 +31,16 @@ final class AddEditFavoriteViewController: NSViewController { private var cancellables = Set() + static func create(bookmark: Bookmark? = nil) -> AddEditFavoriteViewController { + NSStoryboard(name: "AddEditFavoriteViewController", bundle: .main).instantiateInitialController { + let addEditFavoriteViewController = AddEditFavoriteViewController.init(coder: $0) + if let bookmark { + addEditFavoriteViewController?.edit(bookmark: bookmark) + } + return addEditFavoriteViewController + }! + } + override func viewDidLoad() { super.viewDidLoad() diff --git a/DuckDuckGo/HomePage/View/HomePageViewController.swift b/DuckDuckGo/HomePage/View/HomePageViewController.swift index 4147eecacf..c15caf443c 100644 --- a/DuckDuckGo/HomePage/View/HomePageViewController.swift +++ b/DuckDuckGo/HomePage/View/HomePageViewController.swift @@ -37,8 +37,6 @@ final class HomePageViewController: NSViewController { return .init(syncService: syncService, syncBookmarksAdapter: syncBookmarksAdapter) }() - private weak var host: NSView? - var favoritesModel: HomePage.Models.FavoritesModel! var defaultBrowserModel: HomePage.Models.DefaultBrowserModel! var recentlyVisitedModel: HomePage.Models.RecentlyVisitedModel! @@ -53,12 +51,11 @@ final class HomePageViewController: NSViewController { fatalError("HomePageViewController: Bad initializer") } - init?(coder: NSCoder, - tabCollectionViewModel: TabCollectionViewModel, - bookmarkManager: BookmarkManager, - historyCoordinating: HistoryCoordinating = HistoryCoordinator.shared, - fireViewModel: FireViewModel? = nil, - onboardingViewModel: OnboardingViewModel = OnboardingViewModel()) { + init(tabCollectionViewModel: TabCollectionViewModel, + bookmarkManager: BookmarkManager, + historyCoordinating: HistoryCoordinating = HistoryCoordinator.shared, + fireViewModel: FireViewModel? = nil, + onboardingViewModel: OnboardingViewModel = OnboardingViewModel()) { self.tabCollectionViewModel = tabCollectionViewModel self.bookmarkManager = bookmarkManager @@ -66,14 +63,10 @@ final class HomePageViewController: NSViewController { self.fireViewModel = fireViewModel ?? FireCoordinator.fireViewModel self.onboardingViewModel = onboardingViewModel - super.init(coder: coder) + super.init(nibName: nil, bundle: nil) } - override func viewDidLoad() { - super.viewDidLoad() - - refreshModelsOnAppBecomingActive() - + override func loadView() { favoritesModel = createFavoritesModel() defaultBrowserModel = createDefaultBrowserModel() recentlyVisitedModel = createRecentlyVisitedModel() @@ -93,11 +86,11 @@ final class HomePageViewController: NSViewController { self?.view.makeMeFirstResponder() } - let host = NSHostingView(rootView: rootView) - host.frame = view.frame - view.addSubview(host) - self.host = host + self.view = NSHostingView(rootView: rootView) + } + override func viewDidLoad() { + refreshModelsOnAppBecomingActive() subscribeToBookmarks() subscribeToBurningData() } @@ -117,11 +110,6 @@ final class HomePageViewController: NSViewController { refreshModels() } - override func viewDidLayout() { - super.viewDidLayout() - host?.frame = self.view.frame - } - override func viewWillDisappear() { super.viewWillDisappear() @@ -256,32 +244,9 @@ final class HomePageViewController: NSViewController { } private func showAddEditController(for bookmark: Bookmark? = nil) { - // swiftlint:disable force_cast - let windowController = NSStoryboard.homePage.instantiateController(withIdentifier: "AddEditFavoriteWindowController") as! NSWindowController - // swiftlint:enable force_cast + let addEditFavoriteViewController = AddEditFavoriteViewController.create(bookmark: bookmark) - guard let window = windowController.window as? AddEditFavoriteWindow else { - assertionFailure("HomePageViewController: Failed to present AddEditFavoriteWindowController") - return - } - - guard let screen = window.screen else { - assertionFailure("HomePageViewController: No screen") - return - } - - if let bookmark = bookmark { - window.addEditFavoriteViewController.edit(bookmark: bookmark) - } - - let windowFrame = NSRect(x: screen.frame.origin.x + screen.frame.size.width / 2.0 - AddEditFavoriteWindow.Size.width / 2.0, - y: screen.frame.origin.y + screen.frame.size.height / 2.0 - AddEditFavoriteWindow.Size.height / 2.0, - width: AddEditFavoriteWindow.Size.width, - height: AddEditFavoriteWindow.Size.height) - - view.window?.addChildWindow(window, ordered: .above) - window.setFrame(windowFrame, display: true) - window.makeKey() + self.beginSheet(addEditFavoriteViewController) } private var burningDataCancellable: AnyCancellable? @@ -305,9 +270,3 @@ final class HomePageViewController: NSViewController { } } - -fileprivate extension NSStoryboard { - - static let homePage = NSStoryboard(name: "HomePage", bundle: .main) - -} diff --git a/DuckDuckGo/MainWindow/Main.storyboard b/DuckDuckGo/MainWindow/Main.storyboard deleted file mode 100644 index faaf57be5f..0000000000 --- a/DuckDuckGo/MainWindow/Main.storyboard +++ /dev/null @@ -1,178 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/DuckDuckGo/MainWindow/MainViewController.swift b/DuckDuckGo/MainWindow/MainViewController.swift index 4a74cd34d3..f5cfe4a1ea 100644 --- a/DuckDuckGo/MainWindow/MainViewController.swift +++ b/DuckDuckGo/MainWindow/MainViewController.swift @@ -23,23 +23,24 @@ import Common final class MainViewController: NSViewController { - @IBOutlet weak var tabBarContainerView: NSView! - @IBOutlet weak var navigationBarContainerView: NSView! - @IBOutlet weak var webContainerView: NSView! - @IBOutlet weak var findInPageContainerView: NSView! - @IBOutlet weak var bookmarksBarContainerView: NSView! - @IBOutlet var navigationBarTopConstraint: NSLayoutConstraint! - @IBOutlet var addressBarHeightConstraint: NSLayoutConstraint! - @IBOutlet var bookmarksBarHeightConstraint: NSLayoutConstraint! - - @IBOutlet var divider: NSView! - - private(set) var tabBarViewController: TabBarViewController! - private(set) var navigationBarViewController: NavigationBarViewController! - private(set) var browserTabViewController: BrowserTabViewController! - private(set) var findInPageViewController: FindInPageViewController! - private(set) var fireViewController: FireViewController! - private(set) var bookmarksBarViewController: BookmarksBarViewController! + private let tabBarContainerView = NSView() + private let navigationBarContainerView = NSView() + private let webContainerView = NSView() + private let findInPageContainerView = NSView().hidden() + private let bookmarksBarContainerView = NSView() + private let fireContainerView = NSView() + private var navigationBarTopConstraint: NSLayoutConstraint! + private var addressBarHeightConstraint: NSLayoutConstraint! + private var bookmarksBarHeightConstraint: NSLayoutConstraint! + + private let divider = ColorView(frame: .zero, backgroundColor: .separatorColor) + + let tabBarViewController: TabBarViewController + let navigationBarViewController: NavigationBarViewController + let browserTabViewController: BrowserTabViewController + let findInPageViewController: FindInPageViewController + let fireViewController: FireViewController + let bookmarksBarViewController: BookmarksBarViewController let tabCollectionViewModel: TabCollectionViewModel let isBurner: Bool @@ -59,15 +60,98 @@ final class MainViewController: NSViewController { } required init?(coder: NSCoder) { - self.tabCollectionViewModel = TabCollectionViewModel() - self.isBurner = tabCollectionViewModel.isBurner - super.init(coder: coder) + fatalError("MainViewController: Bad initializer") } - init?(coder: NSCoder, tabCollectionViewModel: TabCollectionViewModel) { + init(tabCollectionViewModel: TabCollectionViewModel? = nil, + bookmarkManager: BookmarkManager = LocalBookmarkManager.shared) { + let tabCollectionViewModel = tabCollectionViewModel ?? TabCollectionViewModel() self.tabCollectionViewModel = tabCollectionViewModel self.isBurner = tabCollectionViewModel.isBurner - super.init(coder: coder) + + tabBarViewController = TabBarViewController.create(tabCollectionViewModel: tabCollectionViewModel) + navigationBarViewController = NavigationBarViewController.create(tabCollectionViewModel: tabCollectionViewModel, isBurner: isBurner) + browserTabViewController = BrowserTabViewController.create(tabCollectionViewModel: tabCollectionViewModel) + findInPageViewController = FindInPageViewController.create() + fireViewController = FireViewController.create(tabCollectionViewModel: tabCollectionViewModel) + bookmarksBarViewController = BookmarksBarViewController.create(tabCollectionViewModel: tabCollectionViewModel, bookmarkManager: bookmarkManager) + + super.init(nibName: nil, bundle: nil) + + findInPageViewController.delegate = self + } + + override func loadView() { + view = MainView(frame: NSRect(x: 0, y: 0, width: 600, height: 660)) + + for subview in [ + tabBarContainerView, + divider, + bookmarksBarContainerView, + navigationBarContainerView, + webContainerView, + findInPageContainerView, + fireContainerView, + ] { + subview.translatesAutoresizingMaskIntoConstraints = false + view.addSubview(subview) + } + + addConstraints() + + addAndLayoutChild(tabBarViewController, into: tabBarContainerView) + addAndLayoutChild(bookmarksBarViewController, into: bookmarksBarContainerView) + addAndLayoutChild(navigationBarViewController, into: navigationBarContainerView) + addAndLayoutChild(browserTabViewController, into: webContainerView) + addAndLayoutChild(findInPageViewController, into: findInPageContainerView) + addAndLayoutChild(fireViewController, into: fireContainerView) + } + + private func addConstraints() { + bookmarksBarHeightConstraint = bookmarksBarContainerView.heightAnchor.constraint(equalToConstant: 34) + + navigationBarTopConstraint = navigationBarContainerView.topAnchor.constraint(equalTo: view.topAnchor, constant: 38) + addressBarHeightConstraint = navigationBarContainerView.heightAnchor.constraint(equalToConstant: 42) + + NSLayoutConstraint.activate([ + tabBarContainerView.topAnchor.constraint(equalTo: view.topAnchor), + tabBarContainerView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + tabBarContainerView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + tabBarContainerView.heightAnchor.constraint(equalToConstant: 38), + + divider.topAnchor.constraint(equalTo: navigationBarContainerView.bottomAnchor), + divider.leadingAnchor.constraint(equalTo: view.leadingAnchor), + divider.trailingAnchor.constraint(equalTo: view.trailingAnchor), + divider.heightAnchor.constraint(equalToConstant: 1), + + bookmarksBarContainerView.topAnchor.constraint(equalTo: divider.bottomAnchor), + bookmarksBarContainerView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + bookmarksBarContainerView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + bookmarksBarHeightConstraint, + + navigationBarTopConstraint, + navigationBarContainerView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + navigationBarContainerView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + addressBarHeightConstraint, + + webContainerView.topAnchor.constraint(equalTo: bookmarksBarContainerView.bottomAnchor), + webContainerView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + webContainerView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + webContainerView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + webContainerView.widthAnchor.constraint(greaterThanOrEqualToConstant: 512), + webContainerView.heightAnchor.constraint(greaterThanOrEqualToConstant: 178), + + findInPageContainerView.topAnchor.constraint(equalTo: bookmarksBarContainerView.bottomAnchor, constant: -4), + findInPageContainerView.topAnchor.constraint(equalTo: navigationBarContainerView.bottomAnchor, constant: -4).priority(900), + findInPageContainerView.centerXAnchor.constraint(equalTo: navigationBarContainerView.centerXAnchor), + findInPageContainerView.widthAnchor.constraint(equalToConstant: 400), + findInPageContainerView.heightAnchor.constraint(equalToConstant: 40), + + fireContainerView.topAnchor.constraint(equalTo: view.topAnchor), + fireContainerView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + fireContainerView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + fireContainerView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + ]) } override func viewDidLoad() { @@ -178,62 +262,7 @@ final class MainViewController: NSViewController { func windowWillClose() { eventMonitorCancellables.removeAll() - tabBarViewController?.hideTabPreview() - } - - @IBSegueAction - func createTabBarViewController(coder: NSCoder, sender: Any?, segueIdentifier: String?) -> TabBarViewController? { - guard let tabBarViewController = TabBarViewController(coder: coder, - tabCollectionViewModel: tabCollectionViewModel) else { - fatalError("MainViewController: Failed to init TabBarViewController") - } - - self.tabBarViewController = tabBarViewController - return tabBarViewController - } - - @IBSegueAction - func createNavigationBarViewController(coder: NSCoder, sender: Any?, segueIdentifier: String?) -> NavigationBarViewController? { - guard let navigationBarViewController = NavigationBarViewController(coder: coder, tabCollectionViewModel: tabCollectionViewModel, isBurner: isBurner) else { - fatalError("MainViewController: Failed to init NavigationBarViewController") - } - - self.navigationBarViewController = navigationBarViewController - return navigationBarViewController - } - - @IBSegueAction - func createWebViewController(coder: NSCoder, sender: Any?, segueIdentifier: String?) -> BrowserTabViewController? { - guard let browserTabViewController = BrowserTabViewController(coder: coder, - tabCollectionViewModel: tabCollectionViewModel) else { - fatalError("MainViewController: Failed to init BrowserTabViewController") - } - - self.browserTabViewController = browserTabViewController - return browserTabViewController - } - - @IBSegueAction - func createFindInPageViewController(coder: NSCoder, sender: Any?, segueIdentifier: String?) -> FindInPageViewController? { - let findInPageViewController = FindInPageViewController(coder: coder) - findInPageViewController?.delegate = self - self.findInPageViewController = findInPageViewController - return findInPageViewController - } - - @IBSegueAction - func createFireViewController(coder: NSCoder, sender: Any?, segueIdentifier: String?) -> FireViewController? { - let fireViewController = FireViewController(coder: coder, - tabCollectionViewModel: tabCollectionViewModel) - self.fireViewController = fireViewController - return fireViewController - } - - @IBSegueAction - func createBookmarksBar(coder: NSCoder, sender: Any?, segueIdentifier: String?) -> BookmarksBarViewController? { - let bookmarksBarViewController = BookmarksBarViewController(coder: coder, tabCollectionViewModel: tabCollectionViewModel) - self.bookmarksBarViewController = bookmarksBarViewController - return bookmarksBarViewController + tabBarViewController.hideTabPreview() } func toggleBookmarksBarVisibility() { @@ -256,7 +285,7 @@ final class MainViewController: NSViewController { bookmarksBarViewController.view.removeFromSuperview() } - bookmarksBarHeightConstraint.constant = showBookmarksBar ? 34 : 0 + bookmarksBarHeightConstraint?.constant = showBookmarksBar ? 34 : 0 updateDividerColor() } @@ -265,7 +294,7 @@ final class MainViewController: NSViewController { NSAppearance.withAppAppearance { let isHomePage = tabCollectionViewModel.selectedTabViewModel?.tab.content == .homePage let backgroundColor: NSColor = (bookmarksBarIsVisible || isHomePage) ? .addressBarFocusedBackgroundColor : .addressBarSolidSeparatorColor - (divider as? ColorView)?.backgroundColor = backgroundColor + divider.backgroundColor = backgroundColor } } @@ -362,15 +391,15 @@ final class MainViewController: NSViewController { private func updateFindInPage() { guard let model = tabCollectionViewModel.selectedTabViewModel?.findInPage else { - findInPageViewController?.makeMeFirstResponder() + findInPageViewController.makeMeFirstResponder() os_log("MainViewController: Failed to get find in page model", type: .error) return } findInPageContainerView.isHidden = !model.isVisible - findInPageViewController?.model = model + findInPageViewController.model = model if model.isVisible { - findInPageViewController?.makeMeFirstResponder() + findInPageViewController.makeMeFirstResponder() } } @@ -505,7 +534,7 @@ extension MainViewController { case kVK_Escape: var isHandled = false if !findInPageContainerView.isHidden { - findInPageViewController?.findInPageDone(self) + findInPageViewController.findInPageDone(self) isHandled = true } if let addressBarVC = navigationBarViewController.addressBarViewController { @@ -556,3 +585,27 @@ extension MainViewController { } } + +#if DEBUG +@available(macOS 14.0, *) +#Preview(traits: .fixedLayout(width: 700, height: 660)) { + + let bkman = LocalBookmarkManager(bookmarkStore: BookmarkStoreMock(bookmarks: [ + BookmarkFolder(id: "1", title: "Folder", children: [ + Bookmark(id: "2", url: URL.duckDuckGo.absoluteString, title: "DuckDuckGo", isFavorite: true) + ]), + Bookmark(id: "3", url: URL.duckDuckGo.absoluteString, title: "DuckDuckGo", isFavorite: true, parentFolderUUID: "1") + ])) + bkman.loadBookmarks() + + let vc = MainViewController(bookmarkManager: bkman) + var c: AnyCancellable! + c = vc.publisher(for: \.view.window).sink { window in + window?.titlebarAppearsTransparent = true + window?.titleVisibility = .hidden + withExtendedLifetime(c) {} + } + + return vc +} +#endif diff --git a/DuckDuckGo/MainWindow/MainWindowController.swift b/DuckDuckGo/MainWindow/MainWindowController.swift index ad8d2bce08..56099fe508 100644 --- a/DuckDuckGo/MainWindow/MainWindowController.swift +++ b/DuckDuckGo/MainWindow/MainWindowController.swift @@ -106,10 +106,7 @@ final class MainWindowController: NSWindowController { private var trafficLightsAlphaCancellable: AnyCancellable? private func subscribeToTrafficLightsAlpha() { - guard let tabBarViewController = mainViewController.tabBarViewController else { - assertionFailure("MainWindowController: tabBarViewController is nil" ) - return - } + let tabBarViewController = mainViewController.tabBarViewController // slide tabs to the left in full screen trafficLightsAlphaCancellable = window?.standardWindowButton(.closeButton)? @@ -150,11 +147,11 @@ final class MainWindowController: NSWindowController { } private func moveTabBarView(toTitlebarView: Bool) { - guard let newParentView = toTitlebarView ? titlebarView : mainViewController.view, - let tabBarViewController = mainViewController.tabBarViewController else { + guard let newParentView = toTitlebarView ? titlebarView : mainViewController.view else { assertionFailure("Failed to move tab bar view") return } + let tabBarViewController = mainViewController.tabBarViewController tabBarViewController.view.removeFromSuperview() if toTitlebarView { diff --git a/DuckDuckGo/Menus/MainMenuActions.swift b/DuckDuckGo/Menus/MainMenuActions.swift index 88993890e4..1ec5294a4b 100644 --- a/DuckDuckGo/Menus/MainMenuActions.swift +++ b/DuckDuckGo/Menus/MainMenuActions.swift @@ -278,7 +278,7 @@ extension MainViewController { @objc func openLocation(_ sender: Any?) { makeKeyIfNeeded() - guard let addressBarTextField = navigationBarViewController?.addressBarViewController?.addressBarTextField else { + guard let addressBarTextField = navigationBarViewController.addressBarViewController?.addressBarTextField else { os_log("MainViewController: Cannot reference address bar text field", type: .error) return } @@ -348,9 +348,9 @@ extension MainViewController { } navigationBarViewController = wc.mainViewController.navigationBarViewController } - navigationBarViewController?.view.window?.makeKeyAndOrderFront(nil) + navigationBarViewController.view.window?.makeKeyAndOrderFront(nil) } - navigationBarViewController?.toggleDownloadsPopover(keepButtonVisible: false) + navigationBarViewController.toggleDownloadsPopover(keepButtonVisible: false) } @objc func toggleBookmarksBarFromMenu(_ sender: Any) { @@ -459,7 +459,7 @@ extension MainViewController { } makeKeyIfNeeded() - navigationBarViewController? + navigationBarViewController .addressBarViewController? .addressBarButtonsViewController? .openBookmarkPopover(setFavorite: false, accessPoint: .init(sender: sender, default: .moreMenu)) @@ -472,7 +472,7 @@ extension MainViewController { } makeKeyIfNeeded() - navigationBarViewController? + navigationBarViewController .addressBarViewController? .addressBarButtonsViewController? .openBookmarkPopover(setFavorite: true, accessPoint: .init(sender: sender, default: .moreMenu)) diff --git a/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift b/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift index c63908006e..253dd20f8f 100644 --- a/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift +++ b/DuckDuckGo/NavigationBar/View/NavigationBarViewController.swift @@ -63,6 +63,8 @@ final class NavigationBarViewController: NSViewController { @IBOutlet var buttonsTopConstraint: NSLayoutConstraint! @IBOutlet var logoWidthConstraint: NSLayoutConstraint! + private let downloadListCoordinator: DownloadListCoordinator + lazy var downloadsProgressView: CircularProgressView = { let bounds = downloadsButton.bounds let width: CGFloat = 27.0 @@ -111,12 +113,14 @@ final class NavigationBarViewController: NSViewController { private let networkProtectionFeatureActivation: NetworkProtectionFeatureActivation #endif - required init?(coder: NSCoder) { - fatalError("NavigationBarViewController: Bad initializer") +#if NETWORK_PROTECTION + static func create(tabCollectionViewModel: TabCollectionViewModel, isBurner: Bool, networkProtectionFeatureActivation: NetworkProtectionFeatureActivation = NetworkProtectionKeychainTokenStore(), downloadListCoordinator: DownloadListCoordinator = .shared) -> NavigationBarViewController { + NSStoryboard(name: "NavigationBar", bundle: nil).instantiateInitialController { coder in + self.init(coder: coder, tabCollectionViewModel: tabCollectionViewModel, isBurner: isBurner, networkProtectionFeatureActivation: networkProtectionFeatureActivation, downloadListCoordinator: downloadListCoordinator) + }! } -#if NETWORK_PROTECTION - init?(coder: NSCoder, tabCollectionViewModel: TabCollectionViewModel, isBurner: Bool, networkProtectionFeatureActivation: NetworkProtectionFeatureActivation = NetworkProtectionKeychainTokenStore()) { + init?(coder: NSCoder, tabCollectionViewModel: TabCollectionViewModel, isBurner: Bool, networkProtectionFeatureActivation: NetworkProtectionFeatureActivation, downloadListCoordinator: DownloadListCoordinator) { let vpnBundleID = Bundle.main.vpnMenuAgentBundleId let ipcClient = TunnelControllerIPCClient(machServiceName: vpnBundleID) @@ -129,21 +133,33 @@ final class NavigationBarViewController: NSViewController { self.networkProtectionButtonModel = NetworkProtectionNavBarButtonModel(popoverManager: networkProtectionPopoverManager) self.isBurner = isBurner self.networkProtectionFeatureActivation = networkProtectionFeatureActivation + self.downloadListCoordinator = downloadListCoordinator goBackButtonMenuDelegate = NavigationButtonMenuDelegate(buttonType: .back, tabCollectionViewModel: tabCollectionViewModel) goForwardButtonMenuDelegate = NavigationButtonMenuDelegate(buttonType: .forward, tabCollectionViewModel: tabCollectionViewModel) super.init(coder: coder) } #else - init?(coder: NSCoder, tabCollectionViewModel: TabCollectionViewModel, isBurner: Bool) { + static func create(tabCollectionViewModel: TabCollectionViewModel, isBurner: Bool, downloadListCoordinator: DownloadListCoordinator = .shared) -> NavigationBarViewController { + NSStoryboard(name: "NavigationBar", bundle: nil).instantiateInitialController { coder in + self.init(coder: coder, tabCollectionViewModel: tabCollectionViewModel, isBurner: isBurner, downloadListCoordinator: downloadListCoordinator) + }! + } + + init?(coder: NSCoder, tabCollectionViewModel: TabCollectionViewModel, isBurner: Bool, downloadListCoordinator: DownloadListCoordinator) { self.popovers = NavigationBarPopovers() self.tabCollectionViewModel = tabCollectionViewModel self.isBurner = isBurner + self.downloadListCoordinator = downloadListCoordinator goBackButtonMenuDelegate = NavigationButtonMenuDelegate(buttonType: .back, tabCollectionViewModel: tabCollectionViewModel) goForwardButtonMenuDelegate = NavigationButtonMenuDelegate(buttonType: .forward, tabCollectionViewModel: tabCollectionViewModel) super.init(coder: coder) } #endif + required init?(coder: NSCoder) { + fatalError("NavigationBarViewController: Bad initializer") + } + override func viewDidLoad() { super.viewDidLoad() @@ -582,7 +598,7 @@ final class NavigationBarViewController: NSViewController { } private func subscribeToDownloads() { - DownloadListCoordinator.shared.updates + downloadListCoordinator.updates .throttle(for: 1.0, scheduler: DispatchQueue.main, latest: true) .sink { [weak self] update in guard let self = self else { return } @@ -606,11 +622,11 @@ final class NavigationBarViewController: NSViewController { self.updateDownloadsButton() } .store(in: &downloadsCancellables) - DownloadListCoordinator.shared.progress + downloadListCoordinator.progress .publisher(for: \.fractionCompleted) .throttle(for: 0.2, scheduler: DispatchQueue.main, latest: true) - .map { _ in - let progress = DownloadListCoordinator.shared.progress + .map { [downloadListCoordinator] _ in + let progress = downloadListCoordinator.progress return progress.fractionCompleted == 1.0 || progress.totalUnitCount == 0 ? nil : progress.fractionCompleted } .assign(to: \.progress, onWeaklyHeld: downloadsProgressView) @@ -708,7 +724,7 @@ final class NavigationBarViewController: NSViewController { return } - let hasActiveDownloads = DownloadListCoordinator.shared.hasActiveDownloads + let hasActiveDownloads = downloadListCoordinator.hasActiveDownloads downloadsButton.image = hasActiveDownloads ? Self.Constants.activeDownloadsImage : Self.Constants.inactiveDownloadsImage let isTimerActive = downloadsButtonHidingTimer != nil @@ -754,7 +770,7 @@ final class NavigationBarViewController: NSViewController { private func hideDownloadButtonIfPossible() { if LocalPinningManager.shared.isPinned(.downloads) || - DownloadListCoordinator.shared.hasActiveDownloads || + downloadListCoordinator.hasActiveDownloads || popovers.isDownloadsPopoverShown { return } downloadsButton.isHidden = true diff --git a/DuckDuckGo/Preferences/Model/AutofillPreferencesModel.swift b/DuckDuckGo/Preferences/Model/AutofillPreferencesModel.swift index e77c4434b8..c7f246c0ee 100644 --- a/DuckDuckGo/Preferences/Model/AutofillPreferencesModel.swift +++ b/DuckDuckGo/Preferences/Model/AutofillPreferencesModel.swift @@ -121,7 +121,7 @@ final class AutofillPreferencesModel: ObservableObject { @MainActor func showAutofillPopover(_ selectedCategory: SecureVaultSorting.Category = .allItems) { guard let parentWindowController = WindowControllersManager.shared.lastKeyMainWindowController else { return } - guard let navigationViewController = parentWindowController.mainViewController.navigationBarViewController else { return } + let navigationViewController = parentWindowController.mainViewController.navigationBarViewController navigationViewController.showPasswordManagerPopover(selectedCategory: selectedCategory) } diff --git a/DuckDuckGo/Preferences/Model/SyncPreferences.swift b/DuckDuckGo/Preferences/Model/SyncPreferences.swift index 7c15bd0bab..60933937e5 100644 --- a/DuckDuckGo/Preferences/Model/SyncPreferences.swift +++ b/DuckDuckGo/Preferences/Model/SyncPreferences.swift @@ -194,7 +194,7 @@ final class SyncPreferences: ObservableObject, SyncUI.ManagementViewModel { @MainActor func manageLogins() { guard let parentWindowController = WindowControllersManager.shared.lastKeyMainWindowController else { return } - guard let navigationViewController = parentWindowController.mainViewController.navigationBarViewController else { return } + let navigationViewController = parentWindowController.mainViewController.navigationBarViewController navigationViewController.showPasswordManagerPopover(selectedCategory: .allItems) } diff --git a/DuckDuckGo/Tab/View/Base.lproj/BrowserTab.storyboard b/DuckDuckGo/Tab/View/Base.lproj/BrowserTab.storyboard index 5a36ed9b95..b21cbc5cda 100644 --- a/DuckDuckGo/Tab/View/Base.lproj/BrowserTab.storyboard +++ b/DuckDuckGo/Tab/View/Base.lproj/BrowserTab.storyboard @@ -1,7 +1,7 @@ - + - + @@ -9,18 +9,15 @@ - - + + - - - - - - - - - - - - diff --git a/DuckDuckGo/Tab/View/BrowserTabViewController.swift b/DuckDuckGo/Tab/View/BrowserTabViewController.swift index f337b5d6be..fc216e21c3 100644 --- a/DuckDuckGo/Tab/View/BrowserTabViewController.swift +++ b/DuckDuckGo/Tab/View/BrowserTabViewController.swift @@ -24,11 +24,11 @@ import SwiftUI import BrowserServicesKit final class BrowserTabViewController: NSViewController { - @IBOutlet weak var errorView: NSView! - @IBOutlet weak var homePageView: NSView! - @IBOutlet weak var errorMessageLabel: NSTextField! - @IBOutlet weak var hoverLabel: NSTextField! - @IBOutlet weak var hoverLabelContainer: NSView! + @IBOutlet var errorView: NSView! + @IBOutlet var homePageView: NSView! + @IBOutlet var errorMessageLabel: NSTextField! + @IBOutlet var hoverLabel: NSTextField! + @IBOutlet var hoverLabelContainer: NSView! private weak var webView: WebView? private weak var webViewContainer: NSView? private weak var webViewSnapshot: NSView? @@ -53,6 +53,12 @@ final class BrowserTabViewController: NSViewController { private var transientTabContentViewController: NSViewController? + static func create(tabCollectionViewModel: TabCollectionViewModel) -> BrowserTabViewController { + NSStoryboard(name: "BrowserTab", bundle: nil).instantiateInitialController { coder in + self.init(coder: coder, tabCollectionViewModel: tabCollectionViewModel) + }! + } + required init?(coder: NSCoder) { fatalError("BrowserTabViewController: Bad initializer") } @@ -63,18 +69,12 @@ final class BrowserTabViewController: NSViewController { super.init(coder: coder) } - @IBSegueAction func createHomePageViewController(_ coder: NSCoder) -> NSViewController? { - guard let controller = HomePageViewController(coder: coder, - tabCollectionViewModel: tabCollectionViewModel, - bookmarkManager: LocalBookmarkManager.shared) else { - fatalError("BrowserTabViewController: Failed to init HomePageViewController") - } - return controller - } - override func viewDidLoad() { super.viewDidLoad() + let homePageViewController = HomePageViewController(tabCollectionViewModel: tabCollectionViewModel, bookmarkManager: LocalBookmarkManager.shared) + self.addAndLayoutChild(homePageViewController, into: homePageView) + hoverLabelContainer.alphaValue = 0 subscribeToTabs() subscribeToSelectedTabViewModel() diff --git a/DuckDuckGo/TabBar/View/TabBarViewController.swift b/DuckDuckGo/TabBar/View/TabBarViewController.swift index 0799f7bdb1..56ae7d4279 100644 --- a/DuckDuckGo/TabBar/View/TabBarViewController.swift +++ b/DuckDuckGo/TabBar/View/TabBarViewController.swift @@ -69,6 +69,12 @@ final class TabBarViewController: NSViewController { } } + static func create(tabCollectionViewModel: TabCollectionViewModel) -> TabBarViewController { + NSStoryboard(name: "TabBar", bundle: nil).instantiateInitialController { coder in + self.init(coder: coder, tabCollectionViewModel: tabCollectionViewModel) + }! + } + required init?(coder: NSCoder) { fatalError("TabBarViewController: Bad initializer") } diff --git a/DuckDuckGo/Windows/View/WindowsManager.swift b/DuckDuckGo/Windows/View/WindowsManager.swift index 12cebb760a..d19dad2a42 100644 --- a/DuckDuckGo/Windows/View/WindowsManager.swift +++ b/DuckDuckGo/Windows/View/WindowsManager.swift @@ -123,7 +123,6 @@ final class WindowsManager { private static let defaultPopUpWidth: CGFloat = 1024 private static let defaultPopUpHeight: CGFloat = 752 - private static let fallbackHeadlessScreenFrame = NSRect(x: 0, y: 100, width: 1280, height: 900) class func openPopUpWindow(with tab: Tab, origin: NSPoint?, contentSize: NSSize?) { if let mainWindowController = WindowControllersManager.shared.lastKeyMainWindowController, @@ -133,7 +132,7 @@ final class WindowsManager { mainWindowController.mainViewController.tabCollectionViewModel.insert(tab, selected: true) } else { - let screenFrame = (self.findPositioningSourceWindow(for: tab)?.screen ?? .main)?.visibleFrame ?? Self.fallbackHeadlessScreenFrame + let screenFrame = (self.findPositioningSourceWindow(for: tab)?.screen ?? .main)?.visibleFrame ?? NSScreen.fallbackHeadlessScreenFrame // limit popUp content size to screen visible frame // fallback to default if nil or zero @@ -159,23 +158,7 @@ final class WindowsManager { contentSize: NSSize? = nil, popUp: Bool = false, burnerMode: BurnerMode) -> MainWindowController { - let mainViewController: MainViewController - do { - mainViewController = try NSException.catch { - NSStoryboard(name: "Main", bundle: .main) - .instantiateController(identifier: .mainViewController) { coder -> MainViewController? in - let model = tabCollectionViewModel ?? TabCollectionViewModel(burnerMode: burnerMode) - assert(model.burnerMode == burnerMode) - return MainViewController(coder: coder, tabCollectionViewModel: model) - } - } - } catch { -#if DEBUG - fatalError("WindowsManager.makeNewWindow: \(error)") -#else - fatalError("WindowsManager.makeNewWindow: the App Bundle seems to be removed") -#endif - } + let mainViewController = MainViewController(tabCollectionViewModel: tabCollectionViewModel ?? TabCollectionViewModel(burnerMode: burnerMode)) var contentSize = contentSize ?? NSSize(width: 1024, height: 790) contentSize.width = min(NSScreen.main?.frame.size.width ?? 1024, max(contentSize.width, 300))