From 1322e738ec6c2fa77e362a06bedd8bf5d631268a Mon Sep 17 00:00:00 2001 From: Matt Lichtenstein Date: Thu, 21 Nov 2024 15:37:41 -0500 Subject: [PATCH] Add FXIOS-9746 Bookmarks panel empty states (#23267) --- firefox-ios/Client.xcodeproj/project.pbxproj | 4 + .../Client/Application/ImageIdentifiers.swift | 2 + .../Contents.json | 12 +++ .../MobileBookmarks_folder.pdf | Bin 0 -> 6562 bytes .../noBookmarksInRoot.imageset/Contents.json | 12 +++ .../noBookmarks.pdf | Bin 0 -> 13484 bytes .../BookmarksFolderEmptyStateView.swift | 95 ++++++++++++++++++ .../Bookmarks/BookmarksViewController.swift | 42 ++++++++ firefox-ios/Client/Frontend/Strings.swift | 29 +++++- 9 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 firefox-ios/Client/Assets/Images.xcassets/noBookmarksInFolder.imageset/Contents.json create mode 100644 firefox-ios/Client/Assets/Images.xcassets/noBookmarksInFolder.imageset/MobileBookmarks_folder.pdf create mode 100644 firefox-ios/Client/Assets/Images.xcassets/noBookmarksInRoot.imageset/Contents.json create mode 100644 firefox-ios/Client/Assets/Images.xcassets/noBookmarksInRoot.imageset/noBookmarks.pdf create mode 100644 firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksFolderEmptyStateView.swift diff --git a/firefox-ios/Client.xcodeproj/project.pbxproj b/firefox-ios/Client.xcodeproj/project.pbxproj index 3a54983a7c77..d4d2e5ab7764 100644 --- a/firefox-ios/Client.xcodeproj/project.pbxproj +++ b/firefox-ios/Client.xcodeproj/project.pbxproj @@ -1245,6 +1245,7 @@ C4F3B29A1CFCF93A00966259 /* ButtonToast.swift in Sources */ = {isa = PBXBuildFile; fileRef = C4F3B2991CFCF93A00966259 /* ButtonToast.swift */; }; C706CBEF2C3F0FDE00DC65F1 /* CreditCardSettingsViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C706CBEE2C3F0FDE00DC65F1 /* CreditCardSettingsViewControllerTests.swift */; }; C787D8C32C1CB77900940123 /* FirefoxAccountSignInViewControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C787D8C22C1CB77900940123 /* FirefoxAccountSignInViewControllerTests.swift */; }; + C7CDBEFD2CE6926900826A92 /* BookmarksFolderEmptyStateView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7CDBEFC2CE6926900826A92 /* BookmarksFolderEmptyStateView.swift */; }; C80685D126A0C93900DCD895 /* UserResearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80685D026A0C93900DCD895 /* UserResearch.swift */; }; C807CCCC28367446008E6A5A /* FeatureFlagManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C807CCCB28367446008E6A5A /* FeatureFlagManagerTests.swift */; }; C80C11EE28B3C8B80062922A /* WallpaperMetadataTrackerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80C11ED28B3C8B80062922A /* WallpaperMetadataTrackerTests.swift */; }; @@ -8177,6 +8178,7 @@ C787D8C22C1CB77900940123 /* FirefoxAccountSignInViewControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirefoxAccountSignInViewControllerTests.swift; sourceTree = ""; }; C7A6413B89FE82B94F735FFD /* gl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = gl; path = gl.lproj/ClearHistoryConfirm.strings; sourceTree = ""; }; C7C54345A164D550CF2BC575 /* kn */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = kn; path = kn.lproj/3DTouchActions.strings; sourceTree = ""; }; + C7CDBEFC2CE6926900826A92 /* BookmarksFolderEmptyStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksFolderEmptyStateView.swift; sourceTree = ""; }; C7D341F8BA6A9E44518C11C8 /* oc */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = oc; path = oc.lproj/3DTouchActions.strings; sourceTree = ""; }; C7DB49358A6A84AA7391EEE6 /* ta */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ta; path = ta.lproj/Intro.strings; sourceTree = ""; }; C80685D026A0C93900DCD895 /* UserResearch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserResearch.swift; sourceTree = ""; }; @@ -11168,6 +11170,7 @@ 0B8BF3732CA2EB3300E9812D /* Edit Bookmark */, 0BBC50962CA1F86F00CB7248 /* Legacy */, 0BBC50942CA1F7F900CB7248 /* BookmarksRefactorFeatureFlagProvider.swift */, + C7CDBEFC2CE6926900826A92 /* BookmarksFolderEmptyStateView.swift */, 0B8BF36F2CA2D60B00E9812D /* BookmarksViewController.swift */, ); path = Bookmarks; @@ -16095,6 +16098,7 @@ C82A94F2269F68ED00624AA7 /* LegacyFeatureFlagsManager.swift in Sources */, C8610DAA2A0EBF7100B79FF1 /* OnboardingCardDelegate.swift in Sources */, 3BB50E111D6274CD004B33DF /* TopSiteItemCell.swift in Sources */, + C7CDBEFD2CE6926900826A92 /* BookmarksFolderEmptyStateView.swift in Sources */, 819656192C80ECFE00E62323 /* MainMenuMiddleware.swift in Sources */, E127313D28B6AD99006F39D2 /* WallpaperSettingsViewModel.swift in Sources */, 8A4593C72BF7BECA002758DE /* MicrosurveyTableViewCell.swift in Sources */, diff --git a/firefox-ios/Client/Application/ImageIdentifiers.swift b/firefox-ios/Client/Application/ImageIdentifiers.swift index 533d35ac554a..023fc166d512 100644 --- a/firefox-ios/Client/Application/ImageIdentifiers.swift +++ b/firefox-ios/Client/Application/ImageIdentifiers.swift @@ -26,6 +26,8 @@ public struct ImageIdentifiers { public static let logoVisa = "logo_visa" public static let menuBadge = "menuBadge" public static let menuWarningMask = "warning-mask" + public static let noBookmarksInFolder = "noBookmarksInFolder" + public static let noBookmarksInRoot = "noBookmarksInRoot" public static let qrCodeScanBorder = "qrcode-scanBorder" public static let qrCodeScanLine = "qrcode-scanLine" public static let shoppingNoAnalysisImage = "shoppingNoAnalysisImage" diff --git a/firefox-ios/Client/Assets/Images.xcassets/noBookmarksInFolder.imageset/Contents.json b/firefox-ios/Client/Assets/Images.xcassets/noBookmarksInFolder.imageset/Contents.json new file mode 100644 index 000000000000..70c2254128ab --- /dev/null +++ b/firefox-ios/Client/Assets/Images.xcassets/noBookmarksInFolder.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "MobileBookmarks_folder.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Assets/Images.xcassets/noBookmarksInFolder.imageset/MobileBookmarks_folder.pdf b/firefox-ios/Client/Assets/Images.xcassets/noBookmarksInFolder.imageset/MobileBookmarks_folder.pdf new file mode 100644 index 0000000000000000000000000000000000000000..86ea11602cc53ec8fc33db398f62846144c46980 GIT binary patch literal 6562 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 literal 0 HcmV?d00001 diff --git a/firefox-ios/Client/Assets/Images.xcassets/noBookmarksInRoot.imageset/Contents.json b/firefox-ios/Client/Assets/Images.xcassets/noBookmarksInRoot.imageset/Contents.json new file mode 100644 index 000000000000..c56789bd36be --- /dev/null +++ b/firefox-ios/Client/Assets/Images.xcassets/noBookmarksInRoot.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "noBookmarks.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/firefox-ios/Client/Assets/Images.xcassets/noBookmarksInRoot.imageset/noBookmarks.pdf b/firefox-ios/Client/Assets/Images.xcassets/noBookmarksInRoot.imageset/noBookmarks.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d807e3835d45fcd303ab089a4b684506dcfa34b5 GIT binary patch literal 13484 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 literal 0 HcmV?d00001 diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksFolderEmptyStateView.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksFolderEmptyStateView.swift new file mode 100644 index 000000000000..d600f42bf9a2 --- /dev/null +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksFolderEmptyStateView.swift @@ -0,0 +1,95 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/ + +import Foundation +import Common + +final class BookmarksFolderEmptyStateView: UIView, ThemeApplicable { + private struct UX { + static let a11yTopMargin: CGFloat = 16 + static let TitleTopMargin: CGFloat = 16 + static let BodyTopMargin: CGFloat = 8 + static let ContentLeftRightMargins: CGFloat = 16 + static let StackViewWidthMultiplier: CGFloat = 0.9 + static let imageWidth: CGFloat = 200 + } + + private lazy var logoImage: UIImageView = .build { imageView in + imageView.contentMode = .scaleAspectFit + } + + private lazy var titleLabel: UILabel = .build { label in + label.textAlignment = .center + label.font = FXFontStyles.Bold.headline.scaledFont() + label.numberOfLines = 0 + label.adjustsFontForContentSizeCategory = true + } + + private lazy var bodyLabel: UILabel = .build { label in + label.textAlignment = .center + label.font = FXFontStyles.Regular.body.scaledFont() + label.numberOfLines = 0 + label.adjustsFontForContentSizeCategory = true + } + + private lazy var stackViewWrapper: UIStackView = .build { stackView in + stackView.translatesAutoresizingMaskIntoConstraints = false + stackView.axis = .vertical + stackView.alignment = .center + stackView.spacing = 0 + } + + override init(frame: CGRect = .zero) { + super.init(frame: frame) + + setupLayout() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func configure(isRoot: Bool) { + titleLabel.text = isRoot ? .Bookmarks.EmptyState.Root.Title : .Bookmarks.EmptyState.Nested.Title + bodyLabel.text = isRoot ? .Bookmarks.EmptyState.Root.Body : .Bookmarks.EmptyState.Nested.Body + logoImage.image = UIImage(named: isRoot ? ImageIdentifiers.noBookmarksInRoot : ImageIdentifiers.noBookmarksInFolder) + } + + private func setupLayout() { + stackViewWrapper.addArrangedSubview(logoImage) + stackViewWrapper.setCustomSpacing(UX.TitleTopMargin, after: logoImage) + stackViewWrapper.addArrangedSubview(titleLabel) + stackViewWrapper.setCustomSpacing(UX.BodyTopMargin, after: titleLabel) + stackViewWrapper.addArrangedSubview(bodyLabel) + addSubview(stackViewWrapper) + + let aspectRatio = (logoImage.image?.size.height ?? 1) / (logoImage.image?.size.width ?? 1) + NSLayoutConstraint.activate([ + stackViewWrapper.topAnchor.constraint(greaterThanOrEqualTo: topAnchor, constant: UX.a11yTopMargin), + stackViewWrapper.bottomAnchor.constraint(lessThanOrEqualTo: bottomAnchor), + stackViewWrapper.centerXAnchor.constraint(equalTo: centerXAnchor), + stackViewWrapper.centerYAnchor.constraint(equalTo: centerYAnchor), + stackViewWrapper.widthAnchor.constraint(equalTo: widthAnchor, multiplier: UX.StackViewWidthMultiplier), + + titleLabel.leadingAnchor.constraint( + equalTo: stackViewWrapper.leadingAnchor, constant: UX.ContentLeftRightMargins), + titleLabel.trailingAnchor.constraint( + equalTo: stackViewWrapper.trailingAnchor, constant: -UX.ContentLeftRightMargins), + + bodyLabel.leadingAnchor.constraint( + equalTo: stackViewWrapper.leadingAnchor, constant: UX.ContentLeftRightMargins), + bodyLabel.trailingAnchor.constraint( + equalTo: stackViewWrapper.trailingAnchor, constant: -UX.ContentLeftRightMargins), + + logoImage.widthAnchor.constraint(equalToConstant: UX.imageWidth), + logoImage.heightAnchor.constraint(equalTo: logoImage.widthAnchor, multiplier: aspectRatio) + ]) + } + + // MARK: ThemeApplicable + func applyTheme(theme: Theme) { + titleLabel.textColor = theme.colors.textPrimary + bodyLabel.textColor = theme.colors.textPrimary + } +} diff --git a/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksViewController.swift b/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksViewController.swift index 90ce7a5ead5e..a4635c2d7945 100644 --- a/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksViewController.swift +++ b/firefox-ios/Client/Frontend/Library/Bookmarks/BookmarksViewController.swift @@ -81,6 +81,10 @@ class BookmarksViewController: SiteTableViewController, return button }() + private lazy var emptyStateView: BookmarksFolderEmptyStateView = .build() + + private lazy var a11yEmptyStateScrollView: UIScrollView = .build() + // MARK: - Init init(viewModel: BookmarksPanelViewModel, @@ -119,6 +123,8 @@ class BookmarksViewController: SiteTableViewController, tableView.accessibilityIdentifier = AccessibilityIdentifiers.LibraryPanels.BookmarksPanel.tableView tableView.allowsSelectionDuringEditing = true tableView.dragInteractionEnabled = false + + setupEmptyStateView() } override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) { @@ -137,6 +143,7 @@ class BookmarksViewController: SiteTableViewController, if self?.viewModel.shouldFlashRow ?? false { self?.flashRow() } + self?.updateEmptyState() } } @@ -185,6 +192,7 @@ class BookmarksViewController: SiteTableViewController, self.tableView.insertRows(at: [indexPath], with: .automatic) self.tableView.endUpdates() + self.updateEmptyState() self.flashRow(at: indexPath) } } @@ -240,6 +248,7 @@ class BookmarksViewController: SiteTableViewController, viewModel.bookmarkNodes.remove(at: indexPath.row) tableView.deleteRows(at: [indexPath], with: .left) tableView.endUpdates() + updateEmptyState() } // MARK: Button Actions helpers @@ -306,6 +315,14 @@ class BookmarksViewController: SiteTableViewController, } } + private func updateEmptyState() { + a11yEmptyStateScrollView.isHidden = !viewModel.bookmarkNodes.isEmpty + if !a11yEmptyStateScrollView.isHidden { + let isRoot = viewModel.bookmarkFolderGUID == BookmarkRoots.MobileFolderGUID + emptyStateView.configure(isRoot: isRoot) + } + } + // MARK: - Long press @objc @@ -329,6 +346,31 @@ class BookmarksViewController: SiteTableViewController, }) } + // MARK: - UI Setup + private func setupEmptyStateView() { + view.addSubview(a11yEmptyStateScrollView) + a11yEmptyStateScrollView.addSubview(emptyStateView) + a11yEmptyStateScrollView.isHidden = true + NSLayoutConstraint.activate( + [ + a11yEmptyStateScrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor), + a11yEmptyStateScrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor), + a11yEmptyStateScrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), + a11yEmptyStateScrollView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor), + + emptyStateView.leadingAnchor.constraint(equalTo: a11yEmptyStateScrollView.contentLayoutGuide.leadingAnchor), + emptyStateView.trailingAnchor.constraint( + equalTo: a11yEmptyStateScrollView.contentLayoutGuide.trailingAnchor), + emptyStateView.topAnchor.constraint(equalTo: a11yEmptyStateScrollView.contentLayoutGuide.topAnchor), + emptyStateView.bottomAnchor.constraint(equalTo: a11yEmptyStateScrollView.contentLayoutGuide.bottomAnchor), + emptyStateView.widthAnchor.constraint(equalTo: a11yEmptyStateScrollView.frameLayoutGuide.widthAnchor), + emptyStateView.heightAnchor.constraint( + greaterThanOrEqualTo: a11yEmptyStateScrollView.frameLayoutGuide.heightAnchor), + ] + ) + emptyStateView.applyTheme(theme: currentTheme()) + } + // MARK: - UITableViewDataSource | UITableViewDelegate func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { diff --git a/firefox-ios/Client/Frontend/Strings.swift b/firefox-ios/Client/Frontend/Strings.swift index b6450714aa3d..a981194ed7e3 100644 --- a/firefox-ios/Client/Frontend/Strings.swift +++ b/firefox-ios/Client/Frontend/Strings.swift @@ -151,7 +151,7 @@ extension String { } } -// MARK: - Bookmarks Menu +// MARK: - Bookmarks Panel extension String { public struct Bookmarks { public struct Menu { @@ -201,6 +201,33 @@ extension String { value: "Delete Bookmark", comment: "The title for the Delete Bookmark button, in the Edit Bookmark popup screen which is summoned from the main menu's Save submenu, which will delete the currently bookmarked site from the user's bookmarks.") } + + public struct EmptyState { + public struct Root { + public static let Title = MZLocalizedString( + key: "Bookmarks.EmptyState.Root.Title.v135", + tableName: "Bookmarks", + value: "No bookmarks yet", + comment: "The title for the placeholder screen shown when there are no saved bookmarks, located at the root level of the bookmarks panel within the libray modal") + public static let Body = MZLocalizedString( + key: "Bookmarks.EmptyState.Root.Body.v135", + tableName: "Bookmarks", + value: "Save sites as you browse. We’ll also grab bookmarks from other synced devices.", + comment: "The body text for the placeholder screen shown when there are no saved bookmarks, located at the root level of the bookmarks panel within the libray modal") + } + public struct Nested { + public static let Title = MZLocalizedString( + key: "Bookmarks.EmptyState.Nested.Title.v135", + tableName: "Bookmarks", + value: "This folder is empty", + comment: "The title for the placeholder screen shown when there are no saved bookmarks, located within a nested subfolder of the bookmarks panel within the libray modal") + public static let Body = MZLocalizedString( + key: "Bookmarks.EmptyState.Nested.Body.v135", + tableName: "Bookmarks", + value: "Add bookmarks as you browse so you can find your favorite sites later.", + comment: "The body text for the placeholder screen shown when there are no saved bookmarks, located within a nested subfolder of the bookmarks panel within the libray modal") + } + } } }