From d8f3e6992cb2b479c560f76b4e1ad4636913b15c Mon Sep 17 00:00:00 2001 From: Fernando Bunn Date: Wed, 18 Dec 2024 12:52:01 -0300 Subject: [PATCH 1/8] Add spring animation --- .../RoundedPageSheetPresentationAnimator.swift | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/DuckDuckGo/RoundedPageContainer/RoundedPageSheetPresentationAnimator.swift b/DuckDuckGo/RoundedPageContainer/RoundedPageSheetPresentationAnimator.swift index 8f5f584591..615971d285 100644 --- a/DuckDuckGo/RoundedPageContainer/RoundedPageSheetPresentationAnimator.swift +++ b/DuckDuckGo/RoundedPageContainer/RoundedPageSheetPresentationAnimator.swift @@ -21,9 +21,11 @@ import UIKit enum AnimatorConstants { static let duration: TimeInterval = 0.4 + static let springDamping: CGFloat = 0.9 + static let springVelocity: CGFloat = 0.5 } -class RoundedPageSheetPresentationAnimator: NSObject, UIViewControllerAnimatedTransitioning { +final class RoundedPageSheetPresentationAnimator: NSObject, UIViewControllerAnimatedTransitioning { func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return AnimatorConstants.duration } @@ -39,7 +41,12 @@ class RoundedPageSheetPresentationAnimator: NSObject, UIViewControllerAnimatedTr toView.alpha = 0 contentView.transform = CGAffineTransform(translationX: 0, y: containerView.bounds.height) - UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { + UIView.animate(withDuration: AnimatorConstants.duration, + delay: 0, + usingSpringWithDamping: AnimatorConstants.springDamping, + initialSpringVelocity: AnimatorConstants.springVelocity, + options: .curveEaseInOut, + animations: { toView.alpha = 1 contentView.transform = .identity }, completion: { finished in @@ -60,7 +67,12 @@ class RoundedPageSheetDismissalAnimator: NSObject, UIViewControllerAnimatedTrans let containerView = transitionContext.containerView - UIView.animate(withDuration: transitionDuration(using: transitionContext), animations: { + UIView.animate(withDuration: AnimatorConstants.duration, + delay: 0, + usingSpringWithDamping: AnimatorConstants.springDamping, + initialSpringVelocity: AnimatorConstants.springVelocity, + options: .curveEaseInOut, + animations: { fromView.alpha = 0 contentView.transform = CGAffineTransform(translationX: 0, y: containerView.bounds.height) }, completion: { finished in From dab13b6ad9d431ddbc73cc2b94416a0d36a41e15 Mon Sep 17 00:00:00 2001 From: Fernando Bunn Date: Wed, 18 Dec 2024 13:31:49 -0300 Subject: [PATCH 2/8] Drag animation --- ...ndedPageSheetContainerViewController.swift | 42 ++++++++++++++++++- ...RoundedPageSheetPresentationAnimator.swift | 36 +++++++++++++++- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/DuckDuckGo/RoundedPageContainer/RoundedPageSheetContainerViewController.swift b/DuckDuckGo/RoundedPageContainer/RoundedPageSheetContainerViewController.swift index a9cb54dc7e..a94e349c8e 100644 --- a/DuckDuckGo/RoundedPageContainer/RoundedPageSheetContainerViewController.swift +++ b/DuckDuckGo/RoundedPageContainer/RoundedPageSheetContainerViewController.swift @@ -25,6 +25,9 @@ final class RoundedPageSheetContainerViewController: UIViewController { private let titleText: String private let allowedOrientation: UIInterfaceOrientationMask + private var interactiveDismissalTransition: UIPercentDrivenInteractiveTransition? + private var isInteractiveDismissal = false + private lazy var titleBarView: TitleBarView = { let titleBarView = TitleBarView(logoImage: logoImage, title: titleText) { [weak self] in self?.closeController() @@ -76,6 +79,38 @@ final class RoundedPageSheetContainerViewController: UIViewController { titleBarView.trailingAnchor.constraint(equalTo: view.trailingAnchor), titleBarView.heightAnchor.constraint(equalToConstant: 44) ]) + + let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:))) + titleBarView.addGestureRecognizer(panGesture) + } + + @objc private func handlePanGesture(_ gesture: UIPanGestureRecognizer) { + let translation = gesture.translation(in: view) + let velocity = gesture.velocity(in: view) + let progress = translation.y / view.bounds.height + + switch gesture.state { + case .began: + isInteractiveDismissal = true + interactiveDismissalTransition = UIPercentDrivenInteractiveTransition() + dismiss(animated: true, completion: nil) + case .changed: + interactiveDismissalTransition?.update(progress) + case .ended, .cancelled: + let shouldDismiss = progress > 0.3 || velocity.y > 1000 + if shouldDismiss { + interactiveDismissalTransition?.finish() + } else { + interactiveDismissalTransition?.cancel() + UIView.animate(withDuration: 0.2, animations: { + self.view.transform = .identity + }) + } + isInteractiveDismissal = false + interactiveDismissalTransition = nil + default: + break + } } private func setupContentViewController() { @@ -84,7 +119,7 @@ final class RoundedPageSheetContainerViewController: UIViewController { contentViewController.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - contentViewController.view.topAnchor.constraint(equalTo: titleBarView.bottomAnchor), // Below the title bar + contentViewController.view.topAnchor.constraint(equalTo: titleBarView.bottomAnchor), contentViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), contentViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), contentViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor) @@ -110,6 +145,10 @@ extension RoundedPageSheetContainerViewController: UIViewControllerTransitioning func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? { return RoundedPageSheetDismissalAnimator() } + + func interactionControllerForDismissal(using animator: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning? { + return isInteractiveDismissal ? interactiveDismissalTransition : nil + } } final private class TitleBarView: UIView { @@ -170,7 +209,6 @@ final private class TitleBarView: UIView { self.closeAction = closeAction } - private var closeAction: (() -> Void)? @objc private func closeButtonTapped() { diff --git a/DuckDuckGo/RoundedPageContainer/RoundedPageSheetPresentationAnimator.swift b/DuckDuckGo/RoundedPageContainer/RoundedPageSheetPresentationAnimator.swift index 615971d285..f86aab1bcd 100644 --- a/DuckDuckGo/RoundedPageContainer/RoundedPageSheetPresentationAnimator.swift +++ b/DuckDuckGo/RoundedPageContainer/RoundedPageSheetPresentationAnimator.swift @@ -54,8 +54,9 @@ final class RoundedPageSheetPresentationAnimator: NSObject, UIViewControllerAnim }) } } - class RoundedPageSheetDismissalAnimator: NSObject, UIViewControllerAnimatedTransitioning { + private var animator: UIViewPropertyAnimator? + func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval { return AnimatorConstants.duration } @@ -80,4 +81,37 @@ class RoundedPageSheetDismissalAnimator: NSObject, UIViewControllerAnimatedTrans transitionContext.completeTransition(finished) }) } + + func interruptibleAnimator(using transitionContext: UIViewControllerContextTransitioning) -> UIViewImplicitlyAnimating { + if let existingAnimator = animator { + return existingAnimator + } + + guard let fromViewController = transitionContext.viewController(forKey: .from) as? RoundedPageSheetContainerViewController, + let fromView = fromViewController.view, + let contentView = fromViewController.contentViewController.view else { + fatalError("Invalid view controller setup") + } + + let containerView = transitionContext.containerView + + let animator = UIViewPropertyAnimator(duration: AnimatorConstants.duration, + dampingRatio: AnimatorConstants.springDamping) { + fromView.alpha = 0 + contentView.transform = CGAffineTransform(translationX: 0, y: containerView.bounds.height) + } + + animator.addCompletion { position in + switch position { + case .end: + fromView.removeFromSuperview() + transitionContext.completeTransition(true) + default: + transitionContext.completeTransition(false) + } + } + + self.animator = animator + return animator + } } From c962d5af806babda3055f26fe8db90a125f420b0 Mon Sep 17 00:00:00 2001 From: Fernando Bunn Date: Wed, 18 Dec 2024 15:42:56 -0300 Subject: [PATCH 3/8] Change titlebar UI --- DuckDuckGo/MainViewController.swift | 9 +- ...ndedPageSheetContainerViewController.swift | 99 +------------------ DuckDuckGo/UserText.swift | 1 - DuckDuckGo/en.lproj/Localizable.strings | 3 - .../Public API/AIChatViewController.swift | 32 +++++- .../AIChat/Sources/AIChat/TitleBarView.swift | 89 +++++++++++++++++ .../AIChat/Sources/AIChat/UserText.swift | 2 +- 7 files changed, 131 insertions(+), 104 deletions(-) create mode 100644 LocalPackages/AIChat/Sources/AIChat/TitleBarView.swift diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 35c07f3865..cb0c237d9d 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -1712,13 +1712,10 @@ class MainViewController: UIViewController { } private func openAIChat() { - let logoImage = UIImage(named: "Logo") - let title = UserText.aiChatTitle + let roundedPageSheet = RoundedPageSheetContainerViewController( contentViewController: aiChatViewController, - logoImage: logoImage, - title: title, allowedOrientation: .portrait) present(roundedPageSheet, animated: true, completion: nil) @@ -2990,4 +2987,8 @@ extension MainViewController: AIChatViewControllerDelegate { loadUrlInNewTab(url, inheritedAttribution: nil) viewController.dismiss(animated: true) } + + func aiChatViewControllerDidFinish(_ viewController: AIChatViewController) { + viewController.dismiss(animated: true) + } } diff --git a/DuckDuckGo/RoundedPageContainer/RoundedPageSheetContainerViewController.swift b/DuckDuckGo/RoundedPageContainer/RoundedPageSheetContainerViewController.swift index a94e349c8e..fdceff2c95 100644 --- a/DuckDuckGo/RoundedPageContainer/RoundedPageSheetContainerViewController.swift +++ b/DuckDuckGo/RoundedPageContainer/RoundedPageSheetContainerViewController.swift @@ -21,24 +21,13 @@ import UIKit final class RoundedPageSheetContainerViewController: UIViewController { let contentViewController: UIViewController - private let logoImage: UIImage? - private let titleText: String private let allowedOrientation: UIInterfaceOrientationMask private var interactiveDismissalTransition: UIPercentDrivenInteractiveTransition? private var isInteractiveDismissal = false - private lazy var titleBarView: TitleBarView = { - let titleBarView = TitleBarView(logoImage: logoImage, title: titleText) { [weak self] in - self?.closeController() - } - return titleBarView - }() - - init(contentViewController: UIViewController, logoImage: UIImage?, title: String, allowedOrientation: UIInterfaceOrientationMask = .all) { + init(contentViewController: UIViewController, allowedOrientation: UIInterfaceOrientationMask = .all) { self.contentViewController = contentViewController - self.logoImage = logoImage - self.titleText = title self.allowedOrientation = allowedOrientation super.init(nibName: nil, bundle: nil) modalPresentationStyle = .custom @@ -65,25 +54,9 @@ final class RoundedPageSheetContainerViewController: UIViewController { super.viewDidLoad() view.backgroundColor = .black - setupTitleBar() setupContentViewController() } - private func setupTitleBar() { - view.addSubview(titleBarView) - titleBarView.translatesAutoresizingMaskIntoConstraints = false - - NSLayoutConstraint.activate([ - titleBarView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), - titleBarView.leadingAnchor.constraint(equalTo: view.leadingAnchor), - titleBarView.trailingAnchor.constraint(equalTo: view.trailingAnchor), - titleBarView.heightAnchor.constraint(equalToConstant: 44) - ]) - - let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:))) - titleBarView.addGestureRecognizer(panGesture) - } - @objc private func handlePanGesture(_ gesture: UIPanGestureRecognizer) { let translation = gesture.translation(in: view) let velocity = gesture.velocity(in: view) @@ -119,7 +92,7 @@ final class RoundedPageSheetContainerViewController: UIViewController { contentViewController.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - contentViewController.view.topAnchor.constraint(equalTo: titleBarView.bottomAnchor), + contentViewController.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), contentViewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), contentViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), contentViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor) @@ -130,6 +103,9 @@ final class RoundedPageSheetContainerViewController: UIViewController { contentViewController.view.clipsToBounds = true contentViewController.didMove(toParent: self) + + let panGesture = UIPanGestureRecognizer(target: self, action: #selector(handlePanGesture(_:))) + contentViewController.view.addGestureRecognizer(panGesture) } @objc func closeController() { @@ -150,68 +126,3 @@ extension RoundedPageSheetContainerViewController: UIViewControllerTransitioning return isInteractiveDismissal ? interactiveDismissalTransition : nil } } - -final private class TitleBarView: UIView { - private let imageView: UIImageView - private let titleLabel: UILabel - private let closeButton: UIButton - - init(logoImage: UIImage?, title: String, closeAction: @escaping () -> Void) { - imageView = UIImageView(image: logoImage) - titleLabel = UILabel() - closeButton = UIButton(type: .system) - - super.init(frame: .zero) - - setupView(title: title, closeAction: closeAction) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - private func setupView(title: String, closeAction: @escaping () -> Void) { - backgroundColor = .clear - - imageView.contentMode = .scaleAspectFit - imageView.translatesAutoresizingMaskIntoConstraints = false - - let imageSize: CGFloat = 28 - NSLayoutConstraint.activate([ - imageView.widthAnchor.constraint(equalToConstant: imageSize), - imageView.heightAnchor.constraint(equalToConstant: imageSize) - ]) - - titleLabel.text = title - titleLabel.font = UIFont.systemFont(ofSize: 17, weight: .semibold) - titleLabel.textColor = .white - titleLabel.translatesAutoresizingMaskIntoConstraints = false - - closeButton.setImage(UIImage(named: "Close-24"), for: .normal) - closeButton.tintColor = .white - closeButton.translatesAutoresizingMaskIntoConstraints = false - closeButton.addTarget(self, action: #selector(closeButtonTapped), for: .touchUpInside) - - addSubview(imageView) - addSubview(titleLabel) - addSubview(closeButton) - - NSLayoutConstraint.activate([ - imageView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 16), - imageView.centerYAnchor.constraint(equalTo: centerYAnchor), - - titleLabel.leadingAnchor.constraint(equalTo: imageView.trailingAnchor, constant: 8), - titleLabel.centerYAnchor.constraint(equalTo: centerYAnchor), - - closeButton.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor, constant: -16), - closeButton.centerYAnchor.constraint(equalTo: centerYAnchor) - ]) - - self.closeAction = closeAction - } - private var closeAction: (() -> Void)? - - @objc private func closeButtonTapped() { - closeAction?() - } -} diff --git a/DuckDuckGo/UserText.swift b/DuckDuckGo/UserText.swift index 86a2337975..c6e7a5722f 100644 --- a/DuckDuckGo/UserText.swift +++ b/DuckDuckGo/UserText.swift @@ -1352,7 +1352,6 @@ But if you *do* want a peek under the hood, you can find more information about static let duckPlayerContingencyMessageCTA = NSLocalizedString("duck-player.video-contingency-cta", value: "Learn More", comment: "Button for the message explaining to the user that Duck Player is not available so the user can learn more") // MARK: - AI Chat - public static let aiChatTitle = NSLocalizedString("aichat.title", value: "DuckDuckGo AI Chat", comment: "Title for DuckDuckGo AI Chat. Should not be translated") public static let aiChatFeatureName = NSLocalizedString("aichat.settings.title", value: "AI Chat", comment: "Settings screen cell text for AI Chat settings") public static let aiChatSettingsEnableFooter = NSLocalizedString("aichat.settings.enable.footer", value: "Turning this off will hide the AI Chat feature in the DuckDuckGo app.", comment: "Footer text for AI Chat settings") diff --git a/DuckDuckGo/en.lproj/Localizable.strings b/DuckDuckGo/en.lproj/Localizable.strings index d1bf6015ea..022fc631d0 100644 --- a/DuckDuckGo/en.lproj/Localizable.strings +++ b/DuckDuckGo/en.lproj/Localizable.strings @@ -158,9 +158,6 @@ /* Settings screen cell text for AI Chat settings */ "aichat.settings.title" = "AI Chat"; -/* Title for DuckDuckGo AI Chat. Should not be translated */ -"aichat.title" = "DuckDuckGo AI Chat"; - /* No comment provided by engineer. */ "alert.message.bookmarkAll" = "Existing bookmarks will not be duplicated."; diff --git a/LocalPackages/AIChat/Sources/AIChat/Public API/AIChatViewController.swift b/LocalPackages/AIChat/Sources/AIChat/Public API/AIChatViewController.swift index 31d394969c..2d4bddede5 100644 --- a/LocalPackages/AIChat/Sources/AIChat/Public API/AIChatViewController.swift +++ b/LocalPackages/AIChat/Sources/AIChat/Public API/AIChatViewController.swift @@ -16,6 +16,7 @@ // See the License for the specific language governing permissions and // limitations under the License. // + import UIKit import Combine import WebKit @@ -28,6 +29,11 @@ public protocol AIChatViewControllerDelegate: AnyObject { /// - viewController: The `AIChatViewController` instance making the request. /// - url: The `URL` that is requested to be loaded. func aiChatViewController(_ viewController: AIChatViewController, didRequestToLoad url: URL) + + /// Tells the delegate that the `AIChatViewController` has finished its task. + /// + /// - Parameter viewController: The `AIChatViewController` instance that has finished. + func aiChatViewControllerDidFinish(_ viewController: AIChatViewController) } public final class AIChatViewController: UIViewController { @@ -35,6 +41,17 @@ public final class AIChatViewController: UIViewController { private let chatModel: AIChatViewModeling private var webViewController: AIChatWebViewController? + private lazy var titleBarView: TitleBarView = { + let title = UserText.aiChatTitle + + let titleBarView = TitleBarView(title: UserText.aiChatTitle) { [weak self] in + guard let self = self else { return } + self.delegate?.aiChatViewControllerDidFinish(self) + } + return titleBarView + }() + + /// Initializes a new instance of `AIChatViewController` with the specified remote settings and web view configuration. /// /// - Parameters: @@ -62,6 +79,7 @@ extension AIChatViewController { public override func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = .black + setupTitleBar() } public override func viewWillAppear(_ animated: Bool) { @@ -87,6 +105,18 @@ extension AIChatViewController { // MARK: - Views Setup extension AIChatViewController { + private func setupTitleBar() { + view.addSubview(titleBarView) + titleBarView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + titleBarView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + titleBarView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + titleBarView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + titleBarView.heightAnchor.constraint(equalToConstant: 68) + ]) + } + private func addWebViewController() { guard webViewController == nil else { return } @@ -99,7 +129,7 @@ extension AIChatViewController { viewController.view.translatesAutoresizingMaskIntoConstraints = false NSLayoutConstraint.activate([ - viewController.view.topAnchor.constraint(equalTo: view.topAnchor), + viewController.view.topAnchor.constraint(equalTo: titleBarView.bottomAnchor), viewController.view.bottomAnchor.constraint(equalTo: view.bottomAnchor), viewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor), viewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor) diff --git a/LocalPackages/AIChat/Sources/AIChat/TitleBarView.swift b/LocalPackages/AIChat/Sources/AIChat/TitleBarView.swift new file mode 100644 index 0000000000..c6542bad58 --- /dev/null +++ b/LocalPackages/AIChat/Sources/AIChat/TitleBarView.swift @@ -0,0 +1,89 @@ +// +// TitleBarView.swift +// DuckDuckGo +// +// 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 UIKit + +final class TitleBarView: UIView { + private let titleLabel: UILabel + private let closeButton: UIButton + private let handleBar: UIView + private var closeAction: (() -> Void)? + + init(title: String, closeAction: @escaping () -> Void) { + titleLabel = UILabel() + closeButton = UIButton(type: .system) + handleBar = UIView() + + self.closeAction = closeAction + + super.init(frame: .zero) + + setupView(title: title) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + private func setupView(title: String) { + backgroundColor = .webViewBackgroundColor + + handleBar.backgroundColor = UIColor(white: 0.5, alpha: 0.5) + handleBar.layer.cornerRadius = Constants.handlebarHeight / 2 + handleBar.translatesAutoresizingMaskIntoConstraints = false + + titleLabel.text = title + titleLabel.font = UIFont.systemFont(ofSize: 17, weight: .semibold) + titleLabel.textColor = .label + titleLabel.translatesAutoresizingMaskIntoConstraints = false + + closeButton.setImage(UIImage(named: "Close-24"), for: .normal) + closeButton.tintColor = .label + closeButton.translatesAutoresizingMaskIntoConstraints = false + closeButton.addTarget(self, action: #selector(closeButtonTapped), for: .touchUpInside) + + addSubview(handleBar) + addSubview(titleLabel) + addSubview(closeButton) + + NSLayoutConstraint.activate([ + handleBar.topAnchor.constraint(equalTo: topAnchor, constant: 8), + handleBar.centerXAnchor.constraint(equalTo: centerXAnchor), + handleBar.widthAnchor.constraint(equalToConstant: Constants.handlebarWidth), + handleBar.heightAnchor.constraint(equalToConstant: Constants.handlebarHeight), + + titleLabel.centerXAnchor.constraint(equalTo: safeAreaLayoutGuide.centerXAnchor), + titleLabel.centerYAnchor.constraint(equalTo: handleBar.bottomAnchor, constant: 30), + + closeButton.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor, constant: -16), + closeButton.centerYAnchor.constraint(equalTo: titleLabel.centerYAnchor), + closeButton.widthAnchor.constraint(equalToConstant: Constants.closeButtonSize), + closeButton.heightAnchor.constraint(equalToConstant: Constants.closeButtonSize) + ]) + } + + @objc private func closeButtonTapped() { + closeAction?() + } +} + +private enum Constants { + static let closeButtonSize: CGFloat = 44 + static let handlebarHeight: CGFloat = 3 + static let handlebarWidth: CGFloat = 42 +} diff --git a/LocalPackages/AIChat/Sources/AIChat/UserText.swift b/LocalPackages/AIChat/Sources/AIChat/UserText.swift index d33e745d2d..e88c31793c 100644 --- a/LocalPackages/AIChat/Sources/AIChat/UserText.swift +++ b/LocalPackages/AIChat/Sources/AIChat/UserText.swift @@ -20,5 +20,5 @@ import Foundation public struct UserText { - public static let aiChatTitle = NSLocalizedString("aichat.title", value: "DuckDuckGo AI Chat", comment: "Title for DuckDuckGo AI Chat. Should not be translated") + public static let aiChatTitle = NSLocalizedString("aichat.title", value: "Duck.ai", comment: "Title for DuckDuckGo AI Chat. Should not be translated") } From b5079791973ba36313e0beb2ebb00705e7547a87 Mon Sep 17 00:00:00 2001 From: Fernando Bunn Date: Wed, 18 Dec 2024 16:00:57 -0300 Subject: [PATCH 4/8] Fix alpha animation --- ...undedPageSheetContainerViewController.swift | 18 +++++++++++++++++- .../RoundedPageSheetPresentationAnimator.swift | 6 ++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/DuckDuckGo/RoundedPageContainer/RoundedPageSheetContainerViewController.swift b/DuckDuckGo/RoundedPageContainer/RoundedPageSheetContainerViewController.swift index fdceff2c95..3ce8888906 100644 --- a/DuckDuckGo/RoundedPageContainer/RoundedPageSheetContainerViewController.swift +++ b/DuckDuckGo/RoundedPageContainer/RoundedPageSheetContainerViewController.swift @@ -22,6 +22,7 @@ import UIKit final class RoundedPageSheetContainerViewController: UIViewController { let contentViewController: UIViewController private let allowedOrientation: UIInterfaceOrientationMask + let backgroundView = UIView() private var interactiveDismissalTransition: UIPercentDrivenInteractiveTransition? private var isInteractiveDismissal = false @@ -52,8 +53,9 @@ final class RoundedPageSheetContainerViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = .black + view.backgroundColor = .clear + setupBackgroundView() setupContentViewController() } @@ -86,6 +88,20 @@ final class RoundedPageSheetContainerViewController: UIViewController { } } + private func setupBackgroundView() { + view.addSubview(backgroundView) + + backgroundView.backgroundColor = .black + backgroundView.translatesAutoresizingMaskIntoConstraints = false + + NSLayoutConstraint.activate([ + backgroundView.topAnchor.constraint(equalTo: view.topAnchor), + backgroundView.bottomAnchor.constraint(equalTo: view.bottomAnchor), + backgroundView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + backgroundView.trailingAnchor.constraint(equalTo: view.trailingAnchor) + ]) + } + private func setupContentViewController() { addChild(contentViewController) view.addSubview(contentViewController.view) diff --git a/DuckDuckGo/RoundedPageContainer/RoundedPageSheetPresentationAnimator.swift b/DuckDuckGo/RoundedPageContainer/RoundedPageSheetPresentationAnimator.swift index f86aab1bcd..85cd7d8787 100644 --- a/DuckDuckGo/RoundedPageContainer/RoundedPageSheetPresentationAnimator.swift +++ b/DuckDuckGo/RoundedPageContainer/RoundedPageSheetPresentationAnimator.swift @@ -66,6 +66,7 @@ class RoundedPageSheetDismissalAnimator: NSObject, UIViewControllerAnimatedTrans let fromView = fromViewController.view, let contentView = fromViewController.contentViewController.view else { return } + let fromBackgroundView = fromViewController.backgroundView let containerView = transitionContext.containerView UIView.animate(withDuration: AnimatorConstants.duration, @@ -74,7 +75,7 @@ class RoundedPageSheetDismissalAnimator: NSObject, UIViewControllerAnimatedTrans initialSpringVelocity: AnimatorConstants.springVelocity, options: .curveEaseInOut, animations: { - fromView.alpha = 0 + fromBackgroundView.alpha = 0 contentView.transform = CGAffineTransform(translationX: 0, y: containerView.bounds.height) }, completion: { finished in fromView.removeFromSuperview() @@ -94,10 +95,11 @@ class RoundedPageSheetDismissalAnimator: NSObject, UIViewControllerAnimatedTrans } let containerView = transitionContext.containerView + let fromBackgroundView = fromViewController.backgroundView let animator = UIViewPropertyAnimator(duration: AnimatorConstants.duration, dampingRatio: AnimatorConstants.springDamping) { - fromView.alpha = 0 + fromBackgroundView.alpha = 0 contentView.transform = CGAffineTransform(translationX: 0, y: containerView.bounds.height) } From 7b04a906289c1d6a887353d4643e713518c887c0 Mon Sep 17 00:00:00 2001 From: Fernando Bunn Date: Wed, 18 Dec 2024 16:19:03 -0300 Subject: [PATCH 5/8] Fix X button --- LocalPackages/AIChat/Sources/AIChat/TitleBarView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LocalPackages/AIChat/Sources/AIChat/TitleBarView.swift b/LocalPackages/AIChat/Sources/AIChat/TitleBarView.swift index c6542bad58..efa737b296 100644 --- a/LocalPackages/AIChat/Sources/AIChat/TitleBarView.swift +++ b/LocalPackages/AIChat/Sources/AIChat/TitleBarView.swift @@ -70,7 +70,7 @@ final class TitleBarView: UIView { titleLabel.centerXAnchor.constraint(equalTo: safeAreaLayoutGuide.centerXAnchor), titleLabel.centerYAnchor.constraint(equalTo: handleBar.bottomAnchor, constant: 30), - closeButton.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor, constant: -16), + closeButton.trailingAnchor.constraint(equalTo: safeAreaLayoutGuide.trailingAnchor, constant: -8), closeButton.centerYAnchor.constraint(equalTo: titleLabel.centerYAnchor), closeButton.widthAnchor.constraint(equalToConstant: Constants.closeButtonSize), closeButton.heightAnchor.constraint(equalToConstant: Constants.closeButtonSize) From 5b589a9cda6557da29a86b348534797551010aeb Mon Sep 17 00:00:00 2001 From: Fernando Bunn Date: Thu, 19 Dec 2024 09:19:07 -0300 Subject: [PATCH 6/8] Update colors --- LocalPackages/AIChat/Package.swift | 3 +++ LocalPackages/AIChat/Sources/AIChat/TitleBarView.swift | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/LocalPackages/AIChat/Package.swift b/LocalPackages/AIChat/Package.swift index 478b4b4705..8f688cd1cc 100644 --- a/LocalPackages/AIChat/Package.swift +++ b/LocalPackages/AIChat/Package.swift @@ -13,6 +13,9 @@ let package = Package( targets: ["AIChat"] ), ], + dependencies: [ + .package(url: "https://github.com/duckduckgo/DesignResourcesKit", exact: "3.3.0") + ], targets: [ .target( name: "AIChat", diff --git a/LocalPackages/AIChat/Sources/AIChat/TitleBarView.swift b/LocalPackages/AIChat/Sources/AIChat/TitleBarView.swift index efa737b296..16f8ad4b5a 100644 --- a/LocalPackages/AIChat/Sources/AIChat/TitleBarView.swift +++ b/LocalPackages/AIChat/Sources/AIChat/TitleBarView.swift @@ -17,6 +17,7 @@ // limitations under the License. // import UIKit +import DesignResourcesKit final class TitleBarView: UIView { private let titleLabel: UILabel @@ -49,11 +50,11 @@ final class TitleBarView: UIView { titleLabel.text = title titleLabel.font = UIFont.systemFont(ofSize: 17, weight: .semibold) - titleLabel.textColor = .label + titleLabel.textColor = UIColor(designSystemColor: .textPrimary) titleLabel.translatesAutoresizingMaskIntoConstraints = false closeButton.setImage(UIImage(named: "Close-24"), for: .normal) - closeButton.tintColor = .label + closeButton.tintColor = UIColor(designSystemColor: .icons) closeButton.translatesAutoresizingMaskIntoConstraints = false closeButton.addTarget(self, action: #selector(closeButtonTapped), for: .touchUpInside) From f6ae2598e6b22e760926dae9766e89330e3f3dfe Mon Sep 17 00:00:00 2001 From: Fernando Bunn Date: Thu, 19 Dec 2024 09:33:47 -0300 Subject: [PATCH 7/8] Fix package dependency --- LocalPackages/AIChat/Package.swift | 3 +++ 1 file changed, 3 insertions(+) diff --git a/LocalPackages/AIChat/Package.swift b/LocalPackages/AIChat/Package.swift index 8f688cd1cc..29f3abc3d5 100644 --- a/LocalPackages/AIChat/Package.swift +++ b/LocalPackages/AIChat/Package.swift @@ -19,6 +19,9 @@ let package = Package( targets: [ .target( name: "AIChat", + dependencies: [ + "DesignResourcesKit", + ], resources: [ .process("Resources/Assets.xcassets") ] From ab77bb451be58bf80e497be551ef46794749ff72 Mon Sep 17 00:00:00 2001 From: Fernando Bunn Date: Thu, 19 Dec 2024 09:42:48 -0300 Subject: [PATCH 8/8] Update AI Chat icon --- .../24px/AIChat-24.imageset/AIChat-24.pdf | Bin 1590 -> 4283 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/AIChat-24.imageset/AIChat-24.pdf b/DuckDuckGo/Assets.xcassets/DesignSystemIcons/24px/AIChat-24.imageset/AIChat-24.pdf index b806da0f5968053e90bf582df2ee923e9bbd50b3..731eeaddaa204972e4e3d503a098631dd39b7281 100644 GIT binary patch delta 3925 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 literal 1590 zcmY!laBkm(vTRHc6J~)rRJr8Ji%pz;v2WjoRZWc1%0<1ppRTqlk-zjA+Fc= zNzF?y$xtu`Dh~n*0Zk|_DN0Su<*H!ZI`wp(h@pV%_rIc>-MSuEP2X;FMCwP27~>KZ zp{^s}i|@|dXnT@Paj7Zy`ONFD!*168{`h--{O|AU=ii@iS93e>`ToC|zxUp)TXea9 zd9Ro4yx_e$dB?K$JU-pDDJ7_D{tPyBf?^i0aWy8`>+EvpsK3Ys+ zaekrvE#dbyF3&4^ml`4;{JeF)%k2TDd)wvPIw@_kZ=#ATlh5uIh-dpW$HruSNs7M& zf8D&x6SWe%cJ10_kfC?jHo6w5C{0|>$n*Mh!*LxEg&FpHYCb~+xFJ(rV*pa*f57*VR6z(_enVqbtYU9h1 zxY=>aJag7nV$J!wXO2na%02vL?K4-GqweI4RfozJ#%<8w{54LxI7`NCT0yO3=$qMV z;=QC8^^mRjk}LTKrPf@@_jvnReM9p!wuRLa>!U6%tA27}Re_OFj+|K2^ck%OO6E!5 ztrF6&zN+3jVYY_*dEVc#!n!Wy35V`jnXGI0&hArTpAc5`O5o9!Rf#*wF3&djTHM`G zv7jStnag(esHxE!HnSete4U!i#&q`Z?R%U&|B5+l?$e67NV-P<2dd%=DxqmTK@{u-HE zzh+k5J$UcN?(*ZZAH;L~mob2H3pBaI@(m~>KuQ5~69af|GJ%VN$_B^0y!?`4h3Hsl z9tx=}NLA1eNKA)iD&N$U%tWXB3WaE3c>yXdfaL|8VPpamLa<;a1*I0}mlh?b7At^C zBT$9}0)6NFypq&BppBqR4HFDVECPxtm_mh+vOY)%QIL6OrU0F)0P;bQ0?Z!g{8FG^ zLk#y9Ly7=cV3?tp1oB`p+(MAUARc#4EJ@7CPe;{SQIwj-WuRcr1@S&O2+T}PjZGDR z!cbrc3>C0M9!$u{5NIWekfDJIx++5hGhjfVsxkr^j3#7kU