Skip to content

Commit

Permalink
Merge branch 'main' into juan/implement-fire-window-ui-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
jotaemepereira committed Nov 13, 2024
2 parents 482cc11 + af2ca4b commit 345e508
Show file tree
Hide file tree
Showing 49 changed files with 777 additions and 284 deletions.
15 changes: 12 additions & 3 deletions .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -386,11 +386,20 @@ jobs:
verify-autoconsent-bundle:
name: 'Verify autoconsent bundle'
runs-on: ubuntu-latest

steps:
- name: Skip
- uses: actions/checkout@v4
- uses: actions/setup-node@v3
with:
node-version: 18
cache: 'npm'
- name: Build bundle
run: |
npm ci
npm run rebuild-autoconsent
- name: Verify clean tree
run: |
echo "Skipping autoconsent bundle verification during an experiment"
git update-index --refresh
git diff-index --quiet HEAD --
create-asana-task:
name: Create Asana Task
Expand Down
2 changes: 1 addition & 1 deletion Configuration/BuildNumber.xcconfig
Original file line number Diff line number Diff line change
@@ -1 +1 @@
CURRENT_PROJECT_VERSION = 304
CURRENT_PROJECT_VERSION = 305
135 changes: 74 additions & 61 deletions DuckDuckGo.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/duckduckgo/BrowserServicesKit",
"state" : {
"revision" : "17154907fe86c75942331ed6d037694c666ddd95",
"version" : "208.0.0"
"revision" : "948420e704ea4d9412a4fc3e2c2ab0d5ea5fe5d7",
"version" : "209.1.0"
}
},
{
"identity" : "content-scope-scripts",
"kind" : "remoteSourceControl",
"location" : "https://github.com/duckduckgo/content-scope-scripts",
"state" : {
"revision" : "1733ee59f06f6e725a98cf6cd8322159f59d664b",
"version" : "6.31.0"
"revision" : "adca39c379b1a124f9990e9d0308c374f32f5018",
"version" : "6.32.0"
}
},
{
Expand Down
59 changes: 59 additions & 0 deletions DuckDuckGo/AIChat/AIChatDebugMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,36 @@ import AppKit

final class AIChatDebugMenu: NSMenu {
private var storage = DefaultAIChatPreferencesStorage()
private let customURLLabelMenuItem = NSMenuItem(title: "")
private let debugStorage = AIChatDebugURLSettings()

init() {
super.init(title: "")

buildItems {
NSMenuItem(title: "Reset toolbar onboarding", action: #selector(resetToolbarOnboarding), target: self)
NSMenuItem(title: "Show toolbar onboarding", action: #selector(showToolbarOnboarding), target: self)

NSMenuItem(title: "Web Communication") {
NSMenuItem(title: "Set Custom URL", action: #selector(setCustomURL))
.targetting(self)
NSMenuItem(title: "Reset Custom URL", action: #selector(resetCustomURL))
.targetting(self)
customURLLabelMenuItem
}
}
}

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

// MARK: - Menu State Update

override func update() {
updateWebUIMenuItemsState()
}

@objc func resetToolbarOnboarding() {
storage.reset()
}
Expand All @@ -42,4 +58,47 @@ final class AIChatDebugMenu: NSMenu {
storage.didDisplayAIChatToolbarOnboarding = false
NotificationCenter.default.post(name: .AIChatOpenedForReturningUser, object: nil)
}

@objc func setCustomURL() {
showCustomURLAlert { [weak self] value in

guard let value = value, let url = URL(string: value), url.isValid else { return false }

self?.debugStorage.customURL = value
return true
}
}

@objc func resetCustomURL() {
debugStorage.reset()
updateWebUIMenuItemsState()
}

private func updateWebUIMenuItemsState() {
customURLLabelMenuItem.title = "Custom URL: [\(debugStorage.customURL ?? "")]"
}

private func showCustomURLAlert(callback: @escaping (String?) -> Bool) {
let alert = NSAlert()
alert.messageText = "Enter URL"
alert.addButton(withTitle: "Accept")
alert.addButton(withTitle: "Cancel")

let inputTextField = NSTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 24))
alert.accessoryView = inputTextField

let response = alert.runModal()
if response == .alertFirstButtonReturn {
if !callback(inputTextField.stringValue) {
let invalidAlert = NSAlert()
invalidAlert.messageText = "Invalid URL"
invalidAlert.informativeText = "Please enter a valid URL."
invalidAlert.addButton(withTitle: "OK")
invalidAlert.runModal()
}
} else {
_ = callback(nil)
}
}

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

public protocol AIChatDebugURLSettingsRepresentable {
var customURLHostname: String? { get }
var customURL: String? { get set }
func reset()
}

public final class AIChatDebugURLSettings: AIChatDebugURLSettingsRepresentable {
private let userDefault: UserDefaults

public init(_ userDefault: UserDefaults = .standard) {
self.userDefault = userDefault
}

public var customURLHostname: String? {
if let customURL = customURL,
let url = URL(string: customURL) {
return url.host
}
return nil
}

public var customURL: String? {
get {
userDefault[.customURL]
} set {
userDefault[.customURL] = newValue
}
}

public func reset() {
customURL = nil
}
}

private extension UserDefaults {
enum Key: String {
case customURL
}

subscript<T>(key: Key) -> T? where T: Any {
get {
return value(forKey: key.rawValue) as? T
}
set {
set(newValue, forKey: key.rawValue)
}
}

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

import Common
import UserScript

final class AIChatUserScript: NSObject, Subfeature {

enum MessageNames: String, CaseIterable {
case openSettings
case getUserValues
}

private let handler: AIChatUserScriptHandling
public let featureName: String = "aiChat"
weak var broker: UserScriptMessageBroker?
private(set) var messageOriginPolicy: MessageOriginPolicy

init(handler: AIChatUserScriptHandling, urlSettings: AIChatDebugURLSettingsRepresentable) {
self.handler = handler
var rules = [HostnameMatchingRule]()

/// Default rule for DuckDuckGo AI Chat
rules.append(.exact(hostname: URL.duckDuckGo.absoluteString))

/// Check if a custom hostname is provided in the URL settings
/// Custom hostnames are used for debugging purposes
if let customURLHostname = urlSettings.customURLHostname {
rules.append(.exact(hostname: customURLHostname))
}
self.messageOriginPolicy = .only(rules: rules)
}

func handler(forMethodNamed methodName: String) -> Subfeature.Handler? {
switch MessageNames(rawValue: methodName) {
case .getUserValues:
return handler.handleGetUserValues
case .openSettings:
return handler.openSettings
default:
return nil
}
}
}
48 changes: 48 additions & 0 deletions DuckDuckGo/AIChat/UserScript/AIChatUserScriptHandling.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
//
// AIChatUserScriptHandling.swift
//
// Copyright © 2024 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import UserScript

protocol AIChatUserScriptHandling {
func handleGetUserValues(params: Any, message: UserScriptMessage) -> Encodable?
func openSettings(params: Any, message: UserScriptMessage) async -> Encodable?
}

struct AIChatUserScriptHandler: AIChatUserScriptHandling {

public struct UserValues: Codable {
let isToolbarShortcutEnabled: Bool
let platform: String
}

private let storage: AIChatPreferencesStorage

init(storage: AIChatPreferencesStorage) {
self.storage = storage
}

@MainActor public func openSettings(params: Any, message: UserScriptMessage) -> Encodable? {
WindowControllersManager.shared.showTab(with: .settings(pane: .aiChat))
return nil
}

public func handleGetUserValues(params: Any, message: UserScriptMessage) -> Encodable? {
UserValues(isToolbarShortcutEnabled: storage.shouldDisplayToolbarShortcut,
platform: "macOS")
}
}
9 changes: 9 additions & 0 deletions DuckDuckGo/Application/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
private(set) var stateRestorationManager: AppStateRestorationManager!
private var grammarFeaturesManager = GrammarFeaturesManager()
let internalUserDecider: InternalUserDecider
private var isInternalUserSharingCancellable: AnyCancellable?
let featureFlagger: FeatureFlagger
private var appIconChanger: AppIconChanger!
private var autoClearHandler: AutoClearHandler!
Expand Down Expand Up @@ -433,6 +434,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {

subscribeToEmailProtectionStatusNotifications()
subscribeToDataImportCompleteNotification()
subscribeToInternalUserChanges()

fireFailedCompilationsPixelIfNeeded()

Expand Down Expand Up @@ -748,6 +750,13 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
NotificationCenter.default.addObserver(self, selector: #selector(dataImportCompleteNotification(_:)), name: .dataImportComplete, object: nil)
}

private func subscribeToInternalUserChanges() {
UserDefaults.appConfiguration.isInternalUser = internalUserDecider.isInternalUser

isInternalUserSharingCancellable = internalUserDecider.isInternalUserPublisher
.assign(to: \.isInternalUser, onWeaklyHeld: UserDefaults.appConfiguration)
}

private func emailDidSignInNotification(_ notification: Notification) {
PixelKit.fire(NonStandardEvent(NonStandardPixel.emailEnabled))
if AppDelegate.isNewUser {
Expand Down
51 changes: 0 additions & 51 deletions DuckDuckGo/Autoconsent/AutoconsentExperiment.swift

This file was deleted.

Loading

0 comments on commit 345e508

Please sign in to comment.