Skip to content

Commit

Permalink
Minor Autofill UI updates (#2021)
Browse files Browse the repository at this point in the history
  • Loading branch information
amddg44 authored Oct 4, 2023
1 parent 04f5078 commit 4a45afd
Show file tree
Hide file tree
Showing 33 changed files with 1,180 additions and 216 deletions.
6 changes: 4 additions & 2 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,7 @@
C17B595A2A03AAD30055F2D1 /* PasswordGenerationPromptViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C17B59572A03AAD30055F2D1 /* PasswordGenerationPromptViewController.swift */; };
C17B595B2A03AAD30055F2D1 /* PasswordGenerationPromptView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C17B59582A03AAD30055F2D1 /* PasswordGenerationPromptView.swift */; };
C18ED43C2AB8364400BF3805 /* FileTextPreviewDebugViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C18ED43B2AB8364400BF3805 /* FileTextPreviewDebugViewController.swift */; };
C18ED43A2AB6F77600BF3805 /* AutofillSettingsEnableFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C18ED4392AB6F77600BF3805 /* AutofillSettingsEnableFooterView.swift */; };
C1963863283794A000298D4D /* BookmarksCachingSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1963862283794A000298D4D /* BookmarksCachingSearch.swift */; };
C1B0F63E2AB08904001EAF05 /* PhasedRolloutFeatureFlagTester.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1B0F63D2AB08904001EAF05 /* PhasedRolloutFeatureFlagTester.swift */; };
C1B0F6422AB08BE9001EAF05 /* MockPrivacyConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1B0F6412AB08BE9001EAF05 /* MockPrivacyConfiguration.swift */; };
Expand Down Expand Up @@ -1231,7 +1232,6 @@
317045BF2858C6B90016ED1F /* AutofillInterfaceEmailTruncatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillInterfaceEmailTruncatorTests.swift; sourceTree = "<group>"; };
31794BFF2821DFB600F18633 /* DuckUI */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = DuckUI; sourceTree = "<group>"; };
31951E8D2823003200CAF535 /* AutofillLoginDetailsHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillLoginDetailsHeaderView.swift; sourceTree = "<group>"; };
31951E94282310FF00CAF535 /* WebsiteAccountExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebsiteAccountExtension.swift; sourceTree = "<group>"; };
319A370F28299A850079FBCE /* PasswordHider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasswordHider.swift; sourceTree = "<group>"; };
319A37142829A55F0079FBCE /* AutofillListItemTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillListItemTableViewCell.swift; sourceTree = "<group>"; };
319A37162829C8AD0079FBCE /* UITableViewExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITableViewExtension.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2267,6 +2267,7 @@
C17B59572A03AAD30055F2D1 /* PasswordGenerationPromptViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordGenerationPromptViewController.swift; sourceTree = "<group>"; };
C17B59582A03AAD30055F2D1 /* PasswordGenerationPromptView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PasswordGenerationPromptView.swift; sourceTree = "<group>"; };
C18ED43B2AB8364400BF3805 /* FileTextPreviewDebugViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileTextPreviewDebugViewController.swift; sourceTree = "<group>"; };
C18ED4392AB6F77600BF3805 /* AutofillSettingsEnableFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AutofillSettingsEnableFooterView.swift; sourceTree = "<group>"; };
C1963862283794A000298D4D /* BookmarksCachingSearch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BookmarksCachingSearch.swift; sourceTree = "<group>"; };
C1B0F63D2AB08904001EAF05 /* PhasedRolloutFeatureFlagTester.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhasedRolloutFeatureFlagTester.swift; sourceTree = "<group>"; };
C1B0F63F2AB08A53001EAF05 /* PhasedRolloutFeatureFlagTesterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhasedRolloutFeatureFlagTesterTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3222,6 +3223,7 @@
311BD1AC2836BB3900AEF6C1 /* AutofillItemsEmptyView.swift */,
311BD1AE2836BB4200AEF6C1 /* AutofillItemsLockedView.swift */,
2DC3FBD62FBAF21E87610FA8 /* AutofillNoAuthAvailableView.swift */,
C18ED4392AB6F77600BF3805 /* AutofillSettingsEnableFooterView.swift */,
);
name = Table;
sourceTree = "<group>";
Expand Down Expand Up @@ -5133,7 +5135,6 @@
C13B32D12A0E750700A59236 /* AutofillSettingStatus.swift */,
319A370F28299A850079FBCE /* PasswordHider.swift */,
31C70B5428045E3500FB6AD1 /* SecureVaultErrorReporter.swift */,
31951E94282310FF00CAF535 /* WebsiteAccountExtension.swift */,
F407605328131910006B1E0B /* AutofillLoginUI */,
310C4B4A281B69BC00BA79A9 /* Management */,
C17B59552A03AAC40055F2D1 /* PasswordGeneration */,
Expand Down Expand Up @@ -6376,6 +6377,7 @@
1E4FAA6627D8DFC800ADC5B3 /* CompleteDownloadRowViewModel.swift in Sources */,
83004E862193E5ED00DA013C /* TabViewControllerBrowsingMenuExtension.swift in Sources */,
EE72CA852A862D000043B5B3 /* NetworkProtectionDebugViewController.swift in Sources */,
C18ED43A2AB6F77600BF3805 /* AutofillSettingsEnableFooterView.swift in Sources */,
CB84C7BD29A3EF530088A5B8 /* AppConfigurationURLProvider.swift in Sources */,
AA3D854723D9E88E00788410 /* AppIconSettingsCell.swift in Sources */,
316931D927BD22A80095F5ED /* DownloadActionMessageViewHelper.swift in Sources */,
Expand Down
75 changes: 21 additions & 54 deletions DuckDuckGo/AutofillItemsEmptyView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,10 @@ import DuckUI
class AutofillItemsEmptyView: UIView {

private enum Constants {
static let defaultPadding: CGFloat = 15
static let portraitPaddingImageTitle: CGFloat = 8
static let portraitPaddingTitleSubtitle: CGFloat = 27
static let portraitPaddingTitle: CGFloat = 24
static let imageHeight: CGFloat = 170.0
static let imageWidth: CGFloat = 220.0
static let maxWidth: CGFloat = 250.0
static let topPadding: CGFloat = -64
}

override init(frame: CGRect) {
Expand All @@ -53,24 +53,11 @@ class AutofillItemsEmptyView: UIView {

return label
}()

private lazy var subtitle: UILabel = {
let label = UILabel(frame: CGRect.zero)
label.numberOfLines = 0
label.lineBreakMode = .byWordWrapping
label.textAlignment = .center
label.font = .preferredFont(forTextStyle: .footnote)
label.textColor = .gray70
label.text = UserText.autofillEmptyViewSubtitle

return label
}()

private lazy var imageView: UIImageView = {
let image = UIImage(named: "AutofillKey")
let imageView = UIImageView(image: image)
let imageView = UIImageView(image: .autofillKey)
imageView.contentMode = .scaleAspectFit
imageView.frame = CGRect(x: 0, y: 0, width: 220, height: 170)
imageView.frame = CGRect(x: 0, y: 0, width: Constants.imageWidth, height: Constants.imageHeight)

return imageView
}()
Expand All @@ -81,51 +68,36 @@ class AutofillItemsEmptyView: UIView {
return stackView
}()

private lazy var outerStackContentView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [stackContentView, subtitle])
stackView.axis = .vertical
return stackView
}()

private lazy var centerYConstraint: NSLayoutConstraint = {
NSLayoutConstraint(item: self,
attribute: .centerY,
relatedBy: .equal,
toItem: outerStackContentView,
toItem: stackContentView,
attribute: .centerY,
multiplier: 1.1,
constant: 0)
}()

private lazy var widthConstraintIPhonePortrait: NSLayoutConstraint = {
outerStackContentView.widthAnchor.constraint(equalToConstant: 250)
}()

private lazy var topConstraintIPhonePortrait: NSLayoutConstraint = {
NSLayoutConstraint(item: self,
attribute: .top,
relatedBy: .equal,
toItem: outerStackContentView,
toItem: stackContentView,
attribute: .top,
multiplier: 1,
constant: -66)
constant: Constants.topPadding)
}()

private func installSubviews() {
addSubview(stackContentView)
addSubview(subtitle)
addSubview(outerStackContentView)
}

private func installConstraints() {
stackContentView.translatesAutoresizingMaskIntoConstraints = false
subtitle.translatesAutoresizingMaskIntoConstraints = false
outerStackContentView.translatesAutoresizingMaskIntoConstraints = false

NSLayoutConstraint.activate([
outerStackContentView.centerXAnchor.constraint(equalTo: centerXAnchor),
outerStackContentView.leadingAnchor.constraint(greaterThanOrEqualTo: leadingAnchor, constant: 16),
outerStackContentView.trailingAnchor.constraint(greaterThanOrEqualTo: trailingAnchor, constant: -16)
stackContentView.widthAnchor.constraint(equalToConstant: Constants.maxWidth),
stackContentView.centerXAnchor.constraint(equalTo: centerXAnchor),
])

refreshConstraints()
Expand All @@ -134,27 +106,22 @@ class AutofillItemsEmptyView: UIView {
func refreshConstraints() {
let isIPhonePortrait = traitCollection.verticalSizeClass == .regular && traitCollection.horizontalSizeClass == .compact

if isIPhonePortrait {
centerYConstraint.isActive = !isIPhonePortrait
topConstraintIPhonePortrait.isActive = isIPhonePortrait
widthConstraintIPhonePortrait.isActive = isIPhonePortrait
stackContentView.spacing = Constants.portraitPaddingImageTitle
outerStackContentView.spacing = Constants.portraitPaddingTitleSubtitle + Constants.portraitPaddingTitle
} else {
topConstraintIPhonePortrait.isActive = isIPhonePortrait
widthConstraintIPhonePortrait.isActive = isIPhonePortrait
centerYConstraint.isActive = !isIPhonePortrait
stackContentView.spacing = Constants.defaultPadding
outerStackContentView.spacing = Constants.defaultPadding
}
invalidateIntrinsicContentSize()
centerYConstraint.isActive = !isIPhonePortrait
topConstraintIPhonePortrait.isActive = isIPhonePortrait
}

func adjustHeight(to newHeight: CGFloat) {
frame.size.height = newHeight
}
}

extension AutofillItemsEmptyView: Themable {

func decorate(with theme: Theme) {
title.textColor = theme.autofillDefaultTitleTextColor
subtitle.textColor = theme.autofillDefaultSubtitleTextColor
}
}

private extension UIImage {
static let autofillKey = UIImage(named: "AutofillKey")
}
54 changes: 42 additions & 12 deletions DuckDuckGo/AutofillLoginSettingsListViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,9 @@ final class AutofillLoginSettingsListViewController: UIViewController {
weak var delegate: AutofillLoginSettingsListViewControllerDelegate?
weak var detailsViewController: AutofillLoginDetailsViewController?
private let viewModel: AutofillLoginListViewModel
private let emptyView = AutofillItemsEmptyView()
private lazy var emptyView = AutofillItemsEmptyView()
private let lockedView = AutofillItemsLockedView()
private let enableAutofillFooterView = AutofillSettingsEnableFooterView()
private let emptySearchView = AutofillEmptySearchView()
private let noAuthAvailableView = AutofillNoAuthAvailableView()
private let tld: TLD = AppDependencyProvider.shared.storageCache.tld
Expand Down Expand Up @@ -73,6 +74,7 @@ final class AutofillLoginSettingsListViewController: UIViewController {
tableView.delegate = self
tableView.dataSource = self
tableView.estimatedRowHeight = 60
tableView.estimatedSectionFooterHeight = 40
tableView.registerCell(ofType: AutofillListItemTableViewCell.self)
tableView.registerCell(ofType: EnableAutofillSettingsTableViewCell.self)
// Have to set tableHeaderView height otherwise tableView content will jump when adding / removing searchController due to tableView insetGrouped style
Expand Down Expand Up @@ -154,12 +156,21 @@ final class AutofillLoginSettingsListViewController: UIViewController {
}
}

override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()

guard viewModel.viewState == .empty else { return }
adjustEmptyViewFooterSize()
}

override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransition(to: size, with: coordinator)

coordinator.animate(alongsideTransition: { _ in
self.updateConstraintConstants()
self.emptyView.refreshConstraints()
if self.viewModel.viewState == .empty {
self.emptyView.refreshConstraints()
}
if self.view.subviews.contains(self.noAuthAvailableView) {
self.noAuthAvailableView.refreshConstraints()
}
Expand Down Expand Up @@ -291,38 +302,38 @@ final class AutofillLoginSettingsListViewController: UIViewController {

switch viewModel.viewState {
case .showItems:
emptyView.isHidden = true
tableView.tableFooterView = nil
tableView.isHidden = false
lockedView.isHidden = true
noAuthAvailableView.isHidden = true
emptySearchView.isHidden = true
case .noAuthAvailable:
emptyView.isHidden = true
tableView.tableFooterView = nil
tableView.isHidden = true
lockedView.isHidden = true
noAuthAvailableView.isHidden = false
emptySearchView.isHidden = true
case .authLocked:
emptyView.isHidden = true
tableView.tableFooterView = nil
tableView.isHidden = true
lockedView.isHidden = false
noAuthAvailableView.isHidden = true
emptySearchView.isHidden = true
case .empty:
emptyView.isHidden = false
tableView.tableFooterView = emptyView
tableView.isHidden = false
setEditing(false, animated: false)
lockedView.isHidden = true
noAuthAvailableView.isHidden = true
emptySearchView.isHidden = true
case .searching:
emptyView.isHidden = true
tableView.tableFooterView = nil
tableView.isHidden = false
lockedView.isHidden = true
noAuthAvailableView.isHidden = true
emptySearchView.isHidden = true
case .searchingNoResults:
emptyView.isHidden = true
tableView.tableFooterView = nil
tableView.isHidden = false
lockedView.isHidden = true
noAuthAvailableView.isHidden = true
Expand Down Expand Up @@ -425,12 +436,23 @@ final class AutofillLoginSettingsListViewController: UIViewController {
private func updateConstraintConstants() {
let isIPhoneLandscape = traitCollection.containsTraits(in: UITraitCollection(verticalSizeClass: .compact))
if isIPhoneLandscape {
lockedViewBottomConstraint.constant = (view.frame.height / 2 - max(lockedView.frame.height, 120.0) / 2)
let viewVerticalCenter = view.frame.height / 2
let lockedViewHeight = max(lockedView.frame.height, 120.0)
lockedViewBottomConstraint.constant = viewVerticalCenter - (lockedViewHeight / 2.0)
} else {
lockedViewBottomConstraint.constant = view.frame.height * 0.15
}
}

// Adjust the footer size based on remaining space
private func adjustEmptyViewFooterSize() {
// Temporarily remove the footer
tableView.tableFooterView = nil
let remainingHeight = tableView.frame.height - tableView.contentSize.height - view.safeAreaInsets.bottom - view.safeAreaInsets.top
emptyView.adjustHeight(to: max(remainingHeight, 0))
tableView.tableFooterView = emptyView
}

// MARK: Cell Methods

private func credentialCell(for tableView: UITableView, item: AutofillLoginListItemViewModel, indexPath: IndexPath) -> AutofillListItemTableViewCell {
Expand Down Expand Up @@ -478,7 +500,9 @@ extension AutofillLoginSettingsListViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
switch viewModel.viewState {
case .empty:
return emptyView
return viewModel.sections[section] == .enableAutofill ? enableAutofillFooterView : nil
case .showItems:
return viewModel.sections[section] == .enableAutofill ? enableAutofillFooterView : nil
default:
return nil
}
Expand All @@ -487,9 +511,15 @@ extension AutofillLoginSettingsListViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, heightForFooterInSection section: Int) -> CGFloat {
switch viewModel.viewState {
case .empty:
return max(tableView.bounds.height - tableView.contentSize.height, 250)
if viewModel.sections[section] == .enableAutofill {
return enableAutofillFooterView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
}
return 0
case .showItems:
return 10
if viewModel.sections[section] == .enableAutofill {
return enableAutofillFooterView.systemLayoutSizeFitting(UIView.layoutFittingCompressedSize).height
}
return 10.0
default:
return 0
}
Expand Down
65 changes: 65 additions & 0 deletions DuckDuckGo/AutofillSettingsEnableFooterView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
//
// AutofillSettingsEnableFooterView.swift
// DuckDuckGo
//
// Copyright © 2023 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import UIKit

class AutofillSettingsEnableFooterView: UIView {

private enum Constants {
static let topPadding: CGFloat = 8
static let defaultPadding: CGFloat = 16
}

override init(frame: CGRect) {
super.init(frame: frame)
installSubviews()
installConstraints()
}

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

private lazy var title: UILabel = {
let label = UILabel(frame: CGRect.zero)
label.font = .preferredFont(forTextStyle: .footnote)
label.numberOfLines = 0
label.textAlignment = .left
label.lineBreakMode = .byWordWrapping
label.textColor = UIColor(designSystemColor: .textSecondary)
label.text = UserText.autofillEmptyViewSubtitle

return label
}()

private func installSubviews() {
addSubview(title)
}

private func installConstraints() {
title.translatesAutoresizingMaskIntoConstraints = false

NSLayoutConstraint.activate([
title.topAnchor.constraint(equalTo: self.topAnchor, constant: Constants.topPadding),
title.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: -Constants.defaultPadding),
title.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: Constants.defaultPadding),
title.trailingAnchor.constraint(lessThanOrEqualTo: self.trailingAnchor, constant: -Constants.defaultPadding)
])
}
}
Loading

0 comments on commit 4a45afd

Please sign in to comment.