diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt
index 883ced5602f8..5e128f7bf766 100644
--- a/RELEASE-NOTES.txt
+++ b/RELEASE-NOTES.txt
@@ -5,6 +5,8 @@
* [*] Make it easier to "Share" and "Blaze" a published post with an updated success view [##23128]
* [*] Add support for viewing trashed posts and pages and restoring them from the editor [#23142]
* [*] Impove the "Post Settings" screen groups/ordering to better align with Gutenberg [#23164]
+* [*] Update the "More" menu in the Editor to use modern iOS design and update copy to match Gutenberg [#23145]
+* [*] Update the "Revisions" list design and fix an issue with the footer displaying incorrect "Date Created" for drafts [#23145]
24.8
-----
diff --git a/WordPress/Classes/Models/AbstractPost+TitleForVisibility.swift b/WordPress/Classes/Models/AbstractPost+TitleForVisibility.swift
index d2308bef2a56..e90449a4563a 100644
--- a/WordPress/Classes/Models/AbstractPost+TitleForVisibility.swift
+++ b/WordPress/Classes/Models/AbstractPost+TitleForVisibility.swift
@@ -6,9 +6,15 @@ extension AbstractPost {
static let publicLabel = NSLocalizedString("Public", comment: "Privacy setting for posts set to 'Public' (default). Should be the same as in core WP.")
/// A title describing the status. Ie.: "Public" or "Private" or "Password protected"
- ///
- /// - warning: deprecated (kahu-offline-mode) (use ``PostVisibility``)
@objc var titleForVisibility: String {
+ guard FeatureFlag.syncPublishing.enabled else {
+ return _titleForVisibility
+ }
+ return PostVisibility(post: self).localizedTitle
+ }
+
+ /// - warning: deprecated (kahu-offline-mode) (use ``PostVisibility``)
+ @objc private var _titleForVisibility: String {
if password != nil {
return AbstractPost.passwordProtectedLabel
} else if status == .publishPrivate {
diff --git a/WordPress/Classes/Services/PostCoordinator.swift b/WordPress/Classes/Services/PostCoordinator.swift
index de08dcd4b403..2ea04cc4724e 100644
--- a/WordPress/Classes/Services/PostCoordinator.swift
+++ b/WordPress/Classes/Services/PostCoordinator.swift
@@ -452,6 +452,9 @@ class PostCoordinator: NSObject {
retryDelay = min(32, retryDelay * 1.5)
return retryDelay
}
+ func setLongerDelay() {
+ retryDelay = max(retryDelay, 20)
+ }
var retryDelay: TimeInterval
weak var retryTimer: Timer?
@@ -509,10 +512,11 @@ class PostCoordinator: NSObject {
}
private func startSync(for post: AbstractPost) {
- guard let revision = post.getLatestRevisionNeedingSync() else {
- let worker = getWorker(for: post)
+ if let worker = workers[post.objectID], worker.error != nil {
worker.error = nil
postDidUpdateNotification(for: post)
+ }
+ guard let revision = post.getLatestRevisionNeedingSync() else {
return DDLogInfo("sync: \(post.objectID.shortDescription) is already up to date")
}
startSync(for: post, revision: revision)
@@ -610,14 +614,16 @@ class PostCoordinator: NSObject {
worker.error = error
postDidUpdateNotification(for: operation.post)
- if !PostCoordinator.isTerminalError(error) {
- let delay = worker.nextRetryDelay
- worker.retryTimer = Timer.scheduledTimer(withTimeInterval: delay, repeats: false) { [weak self, weak worker] _ in
- guard let self, let worker else { return }
- self.didRetryTimerFire(for: worker)
- }
- worker.log("scheduled retry with delay: \(delay)s.")
+ if PostCoordinator.isTerminalError(error) {
+ worker.setLongerDelay()
+ }
+
+ let delay = worker.nextRetryDelay
+ worker.retryTimer = Timer.scheduledTimer(withTimeInterval: delay, repeats: false) { [weak self, weak worker] _ in
+ guard let self, let worker else { return }
+ self.didRetryTimerFire(for: worker)
}
+ worker.log("scheduled retry with delay: \(delay)s.")
if let error = error as? PostRepository.PostSaveError, case .deleted = error {
operation.log("post was permanently deleted")
diff --git a/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift b/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift
index 091202eaf25f..7b15048bc74a 100644
--- a/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift
+++ b/WordPress/Classes/Utility/Analytics/WPAnalyticsEvent.swift
@@ -41,6 +41,7 @@ import Foundation
case editorPostSlugChanged
case editorPostExcerptChanged
case editorPostSiteChanged
+ case editorPostLegacyMoreMenuShown
// Resolve post version conflict
case resolveConflictScreenShown
@@ -666,6 +667,8 @@ import Foundation
return "editor_post_excerpt_changed"
case .editorPostSiteChanged:
return "editor_post_site_changed"
+ case .editorPostLegacyMoreMenuShown:
+ return "editor_post_legacy_more_menu_shown"
case .resolveConflictScreenShown:
return "resolve_conflict_screen_shown"
case .resolveConflictSaveTapped:
diff --git a/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecPostViewController.swift b/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecPostViewController.swift
index 3d57500e61b3..86de50e099df 100644
--- a/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecPostViewController.swift
+++ b/WordPress/Classes/ViewRelated/Aztec/ViewControllers/AztecPostViewController.swift
@@ -1197,7 +1197,7 @@ private extension AztecPostViewController {
if (post.revisions ?? []).count > 0 {
alert.addDefaultActionWithTitle(MoreSheetAlert.historyTitle) { [unowned self] _ in
- self.displayHistory()
+ self.displayRevisionsList()
}
}
diff --git a/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController+MoreActions.swift b/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController+MoreActions.swift
index c9f61cbbbfac..e8cf27fa7d1e 100644
--- a/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController+MoreActions.swift
+++ b/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController+MoreActions.swift
@@ -11,7 +11,11 @@ extension GutenbergViewController {
case managedObjectContextMissing = 2
}
+ // - warning: deprecated (kahu-offline-mode)
+ // TODO: Remove when/if confirmed that this is never invoked by Gutenberg.
func displayMoreSheet() {
+ WPAnalytics.track(.editorPostLegacyMoreMenuShown)
+
// Dismisses and locks the Notices Store from displaying any new notices.
ActionDispatcher.dispatch(NoticeAction.lock)
let alert = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
@@ -52,7 +56,7 @@ extension GutenbergViewController {
if (post.revisions ?? []).count > 0 {
alert.addDefaultActionWithTitle(MoreSheetAlert.historyTitle) { [weak self] _ in
- self?.displayHistory()
+ self?.displayRevisionsList()
ActionDispatcher.dispatch(NoticeAction.unlock)
}
}
@@ -86,11 +90,68 @@ extension GutenbergViewController {
present(alert, animated: true)
}
+
+ func makeMoreMenu() -> UIMenu {
+ UIMenu(title: "", image: nil, identifier: nil, options: [], children: [
+ UIDeferredMenuElement.uncached { [weak self] in
+ $0(self?.makeMoreMenuSections() ?? [])
+ }
+ ])
+ }
+
+ private func makeMoreMenuSections() -> [UIMenuElement] {
+ var sections: [UIMenuElement] = [
+ UIMenu(title: "", subtitle: "", options: .displayInline, children: makeMoreMenuActions())
+ ]
+ if let string = makeContextStructureString() {
+ sections.append(UIAction(subtitle: string, attributes: [.disabled], handler: { _ in }))
+ }
+ return sections
+ }
+
+ private func makeMoreMenuActions() -> [UIAction] {
+ var actions: [UIAction] = []
+
+ let toggleModeTitle = mode == .richText ? Strings.codeEditor : Strings.visualEditor
+ let toggleModeIconName = mode == .richText ? "curlybraces" : "doc.richtext"
+ actions.append(UIAction(title: toggleModeTitle, image: UIImage(systemName: toggleModeIconName)) { [weak self] _ in
+ self?.toggleEditingMode()
+ })
+
+ actions.append(UIAction(title: Strings.preview, image: UIImage(systemName: "safari")) { [weak self] _ in
+ self?.displayPreview()
+ })
+
+ let revisionCount = (post.revisions ?? []).count
+ if revisionCount > 0 {
+ actions.append(UIAction(title: Strings.revisions + " (\(revisionCount))", image: UIImage(systemName: "clock.arrow.circlepath")) { [weak self] _ in
+ self?.displayRevisionsList()
+ })
+ }
+
+ let settingsTitle = self.post is Page ? Strings.pageSettings : Strings.postSettings
+ actions.append(UIAction(title: settingsTitle, image: UIImage(systemName: "gearshape")) { [weak self] _ in
+ self?.displayPostSettings()
+ })
+ let helpTitle = JetpackFeaturesRemovalCoordinator.jetpackFeaturesEnabled() ? Strings.helpAndSupport : Strings.help
+ actions.append(UIAction(title: helpTitle, image: UIImage(systemName: "questionmark.circle")) { [weak self] _ in
+ self?.showEditorHelp()
+ })
+ return actions
+ }
+
+ private func makeContextStructureString() -> String? {
+ guard mode == .richText, let contentInfo = contentInfo else {
+ return nil
+ }
+ return String(format: Strings.contentStructure, contentInfo.blockCount, contentInfo.wordCount, contentInfo.characterCount)
+ }
}
// MARK: - Constants
extension GutenbergViewController {
+ // - warning: deprecated (kahu-offline-mode)
struct MoreSheetAlert {
static let htmlTitle = NSLocalizedString("Switch to HTML Mode", comment: "Switches the Editor to HTML Mode")
static let richTitle = NSLocalizedString("Switch to Visual Mode", comment: "Switches the Editor to Rich Text Mode")
@@ -104,3 +165,15 @@ extension GutenbergViewController {
static let editorHelpTitle = NSLocalizedString("Help", comment: "Open editor help options")
}
}
+
+private enum Strings {
+ static let codeEditor = NSLocalizedString("postEditor.moreMenu.codeEditor", value: "Code Editor", comment: "Post Editor / Button in the 'More' menu")
+ static let visualEditor = NSLocalizedString("postEditor.moreMenu.visualEditor", value: "Visual Editor", comment: "Post Editor / Button in the 'More' menu")
+ static let preview = NSLocalizedString("postEditor.moreMenu.preview", value: "Preview", comment: "Post Editor / Button in the 'More' menu")
+ static let revisions = NSLocalizedString("postEditor.moreMenu.revisions", value: "Revisions", comment: "Post Editor / Button in the 'More' menu")
+ static let pageSettings = NSLocalizedString("postEditor.moreMenu.pageSettings", value: "Page Settings", comment: "Post Editor / Button in the 'More' menu")
+ static let postSettings = NSLocalizedString("postEditor.moreMenu.postSettings", value: "Post Settings", comment: "Post Editor / Button in the 'More' menu")
+ static let helpAndSupport = NSLocalizedString("postEditor.moreMenu.helpAndSupport", value: "Help & Support", comment: "Post Editor / Button in the 'More' menu")
+ static let help = NSLocalizedString("postEditor.moreMenu.help", value: "Help", comment: "Post Editor / Button in the 'More' menu")
+ static let contentStructure = NSLocalizedString("postEditor.moreMenu.contentStructure", value: "Blocks: %li, Words: %li, Characters: %li", comment: "Post Editor / 'More' menu details labels with 'Blocks', 'Words' and 'Characters' counts as parameters (in that order)")
+}
diff --git a/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift b/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift
index da8ac1fb1f7e..16eb9741a50a 100644
--- a/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift
+++ b/WordPress/Classes/ViewRelated/Gutenberg/GutenbergViewController.swift
@@ -449,6 +449,15 @@ class GutenbergViewController: UIViewController, PostEditor, FeaturedImageDelega
borderBottom.frame = CGRect(x: 0, y: navigationController?.navigationBar.frame.size.height ?? 0 - borderWidth, width: navigationController?.navigationBar.frame.size.width ?? 0, height: borderWidth)
borderBottom.autoresizingMask = [.flexibleWidth, .flexibleTopMargin]
navigationController?.navigationBar.addSubview(borderBottom)
+
+ if FeatureFlag.syncPublishing.enabled {
+ navigationBarManager.moreButton.menu = makeMoreMenu()
+ navigationBarManager.moreButton.showsMenuAsPrimaryAction = true
+ }
+ }
+
+ @objc private func buttonMoreTapped() {
+ displayMoreSheet()
}
private func reloadBlogIconView() {
diff --git a/WordPress/Classes/ViewRelated/Pages/PageListSectionHeaderView.swift b/WordPress/Classes/ViewRelated/Pages/PageListSectionHeaderView.swift
deleted file mode 100644
index 688e5a2d43ea..000000000000
--- a/WordPress/Classes/ViewRelated/Pages/PageListSectionHeaderView.swift
+++ /dev/null
@@ -1,19 +0,0 @@
-import UIKit
-
-class PageListSectionHeaderView: UIView {
-
- @IBOutlet weak var titleLabel: UILabel!
- @IBOutlet weak var separator: UIView!
-
- func setTitle(_ title: String) {
- titleLabel.text = title.uppercased(with: .current)
- }
-
- override func awakeFromNib() {
- super.awakeFromNib()
-
- backgroundColor = .listBackground
- titleLabel.backgroundColor = .listBackground
- WPStyleGuide.applyBorderStyle(separator)
- }
-}
diff --git a/WordPress/Classes/ViewRelated/Pages/PageListSectionHeaderView.xib b/WordPress/Classes/ViewRelated/Pages/PageListSectionHeaderView.xib
deleted file mode 100644
index 2e5cbbcac673..000000000000
--- a/WordPress/Classes/ViewRelated/Pages/PageListSectionHeaderView.xib
+++ /dev/null
@@ -1,67 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/WordPress/Classes/ViewRelated/Post/AbstractPostListViewController.swift b/WordPress/Classes/ViewRelated/Post/AbstractPostListViewController.swift
index aaa942a40f23..17dd6717a655 100644
--- a/WordPress/Classes/ViewRelated/Post/AbstractPostListViewController.swift
+++ b/WordPress/Classes/ViewRelated/Post/AbstractPostListViewController.swift
@@ -758,7 +758,9 @@ class AbstractPostListViewController: UIViewController,
SiteStatsInformation.sharedInstance.oauth2Token = blog.authToken
SiteStatsInformation.sharedInstance.siteID = blog.dotComID
- let postURL = URL(string: post.permaLink! as String)
+ guard let postURL = post.permaLink.flatMap(URL.init) else {
+ return wpAssertionFailure("permalink missing or invalid")
+ }
let postStatsTableViewController = PostStatsTableViewController.withJPBannerForBlog(postID: postID,
postTitle: post.titleForDisplay(),
postURL: postURL)
diff --git a/WordPress/Classes/ViewRelated/Post/PostCardStatusViewModel.swift b/WordPress/Classes/ViewRelated/Post/PostCardStatusViewModel.swift
index ab5efecccb9a..08c88342ff85 100644
--- a/WordPress/Classes/ViewRelated/Post/PostCardStatusViewModel.swift
+++ b/WordPress/Classes/ViewRelated/Post/PostCardStatusViewModel.swift
@@ -97,14 +97,10 @@ class PostCardStatusViewModel: NSObject, AbstractPostMenuViewModel {
return .warning
}
switch post.status ?? .draft {
- case .pending:
- return .success
- case .scheduled:
- return .primary(.shade40)
case .trash:
return .error
default:
- return .neutral(.shade70)
+ return .secondaryLabel
}
}
@@ -277,9 +273,18 @@ class PostCardStatusViewModel: NSObject, AbstractPostMenuViewModel {
func statusAndBadges(separatedBy separator: String) -> String {
let sticky = post.isStickyPost ? Constants.stickyLabel : ""
let pending = (post.status == .pending && isSyncPublishingEnabled) ? Constants.pendingReview : ""
+ let visibility: String = {
+ let visibility = PostVisibility(post: post)
+ switch visibility {
+ case .public:
+ return ""
+ case .private, .protected:
+ return visibility.localizedTitle
+ }
+ }()
let status = self.status ?? ""
- return [status, pending, sticky].filter { !$0.isEmpty }.joined(separator: separator)
+ return [status, visibility, pending, sticky].filter { !$0.isEmpty }.joined(separator: separator)
}
/// Determine what the failed status message should be and return it.
diff --git a/WordPress/Classes/ViewRelated/Post/PostEditor+MoreOptions.swift b/WordPress/Classes/ViewRelated/Post/PostEditor+MoreOptions.swift
index d4a93768833a..e0e83ebfeaa4 100644
--- a/WordPress/Classes/ViewRelated/Post/PostEditor+MoreOptions.swift
+++ b/WordPress/Classes/ViewRelated/Post/PostEditor+MoreOptions.swift
@@ -157,7 +157,7 @@ extension PostEditor {
}
}
- func displayHistory() {
+ func displayRevisionsList() {
guard FeatureFlag.syncPublishing.enabled else {
_displayHistory()
return
@@ -174,7 +174,7 @@ extension PostEditor {
self.post.mt_excerpt = revision.postExcerpt
// It's important to clear the pending uploads associated with the
- // post. The assumption is that if the revision on the remote,
+ // post. The assumption is that if the revision on the remote,
// its associated media has to be also uploaded.
MediaCoordinator.shared.cancelUploadOfAllMedia(for: self.post)
self.post.media = []
diff --git a/WordPress/Classes/ViewRelated/Post/PostEditor.swift b/WordPress/Classes/ViewRelated/Post/PostEditor.swift
index 0016190ac30e..f86694d5d20d 100644
--- a/WordPress/Classes/ViewRelated/Post/PostEditor.swift
+++ b/WordPress/Classes/ViewRelated/Post/PostEditor.swift
@@ -147,6 +147,7 @@ extension PostEditor where Self: UIViewController {
showPostTrashedOverlay()
} else {
showAutosaveAvailableAlertIfNeeded()
+ showTerminalUploadErrorAlertIfNeeded()
}
var cancellables: [AnyCancellable] = []
@@ -273,6 +274,39 @@ extension PostEditor where Self: UIViewController {
self.post = post // Even if it's the same instance, it's how you currently refresh the editor
self.createRevisionOfPost()
}
+
+ // MARK: - Failed Media Uploads
+
+ private func showTerminalUploadErrorAlertIfNeeded() {
+ let hasTerminalError = post.media.contains {
+ guard let error = $0.error else { return false }
+ return MediaCoordinator.isTerminalError(error)
+ }
+ if hasTerminalError {
+ let notice = Notice(title: Strings.failingMediaUploadsMessage, feedbackType: .error, actionTitle: Strings.failingMediaUploadsViewAction, actionHandler: { [weak self] _ in
+ self?.showMediaUploadDetails()
+ })
+ DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(700)) {
+ ActionDispatcherFacade().dispatch(NoticeAction.post(notice))
+ } // Delay to let the editor show first
+ }
+ }
+
+ private func showMediaUploadDetails() {
+ let viewController = PostMediaUploadsViewController(post: post)
+ let nav = UINavigationController(rootViewController: viewController)
+ nav.navigationBar.isTranslucent = true // Reset to default
+ viewController.navigationItem.leftBarButtonItem = UIBarButtonItem(systemItem: .close, primaryAction: UIAction { [weak self] _ in
+ self?.dismiss(animated: true)
+ })
+ if let sheetController = nav.sheetPresentationController {
+ sheetController.detents = [.medium(), .large()]
+ sheetController.prefersGrabberVisible = true
+ sheetController.preferredCornerRadius = 16
+ nav.additionalSafeAreaInsets = UIEdgeInsets(top: 8, left: 0, bottom: 0, right: 0)
+ }
+ self.present(nav, animated: true)
+ }
}
private var cancellablesKey: UInt8 = 0
@@ -305,4 +339,8 @@ private enum Strings {
static let trashedPostSheetCancel = NSLocalizedString("postEditor.recoverTrashedPostAlert.cancel", value: "Cancel", comment: "Editor, alert for recovering a trashed post")
static let trashedPostSheetRecover = NSLocalizedString("postEditor.recoverTrashedPostAlert.restore", value: "Restore", comment: "Editor, alert for recovering a trashed post")
static let trashedPostRestored = NSLocalizedString("postEditor.recoverTrashedPost.postRecoveredNoticeTitle", value: "Post restored as a draft", comment: "Editor, notice for successful recovery a trashed post")
+
+ static let failingMediaUploadsMessage = NSLocalizedString("postEditor.postHasFailingMediaUploadsSnackbar.message", value: "Some media items failed to upload", comment: "A message for a snackbar informing the user that some media files requires their attention")
+
+ static let failingMediaUploadsViewAction = NSLocalizedString("postEditor.postHasFailingMediaUploadsSnackbar.actionView", value: "View", comment: "A 'View' action for a snackbar informing the user that some media files requires their attention")
}
diff --git a/WordPress/Classes/ViewRelated/Post/PostEditorNavigationBarManager.swift b/WordPress/Classes/ViewRelated/Post/PostEditorNavigationBarManager.swift
index db9fe23d2a50..b3be9854585d 100644
--- a/WordPress/Classes/ViewRelated/Post/PostEditorNavigationBarManager.swift
+++ b/WordPress/Classes/ViewRelated/Post/PostEditorNavigationBarManager.swift
@@ -117,7 +117,7 @@ class PostEditorNavigationBarManager {
return button
}()
- private lazy var moreButton: UIButton = {
+ lazy var moreButton: UIButton = {
let image = UIImage(named: "editor-more")
let button = UIButton(type: .system)
button.setImage(image, for: .normal)
diff --git a/WordPress/Classes/ViewRelated/Post/PostMediaUploadsView.swift b/WordPress/Classes/ViewRelated/Post/PostMediaUploadsView.swift
index a9e4f4a306e3..22818819b471 100644
--- a/WordPress/Classes/ViewRelated/Post/PostMediaUploadsView.swift
+++ b/WordPress/Classes/ViewRelated/Post/PostMediaUploadsView.swift
@@ -1,6 +1,19 @@
import Foundation
import SwiftUI
+final class PostMediaUploadsViewController: UIHostingController {
+ private let viewModel: PostMediaUploadsViewModel
+
+ init(post: AbstractPost) {
+ self.viewModel = PostMediaUploadsViewModel(post: post) // Manange lifecycle
+ super.init(rootView: PostMediaUploadsView(viewModel: viewModel))
+ }
+
+ required dynamic init?(coder aDecoder: NSCoder) {
+ fatalError("init(coder:) has not been implemented")
+ }
+}
+
/// Displays upload progress for the media for the given post.
struct PostMediaUploadsView: View {
@ObservedObject var viewModel: PostMediaUploadsViewModel
diff --git a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift
index 93af5cf6eb91..336f8481866b 100644
--- a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift
+++ b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController+Swift.swift
@@ -2,6 +2,7 @@ import UIKit
import CoreData
import Combine
import WordPressKit
+import SwiftUI
extension PostSettingsViewController {
static func make(for post: AbstractPost) -> PostSettingsViewController {
@@ -178,6 +179,47 @@ extension PostSettingsViewController: UIAdaptivePresentationControllerDelegate {
}
}
+// MARK: - PostSettingsViewController (Visibility)
+
+extension PostSettingsViewController {
+ @objc func showUpdatedPostVisibilityPicker() {
+ let view = PostVisibilityPicker(selection: .init(post: apost)) { [weak self] selection in
+ guard let self else { return }
+
+ WPAnalytics.track(.editorPostVisibilityChanged, properties: ["via": "settings"])
+
+ switch selection.type {
+ case .public, .protected:
+ if self.apost.original().status == .scheduled {
+ // Keep it scheduled
+ } else {
+ self.apost.status = .publish
+ }
+ case .private:
+ if self.apost.original().status == .scheduled {
+ DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(300)) {
+ self.showWarningPostWillBePublishedAlert()
+ }
+ }
+ self.apost.status = .publishPrivate
+ }
+ self.apost.password = selection.password.isEmpty ? nil : selection.password
+ self.navigationController?.popViewController(animated: true)
+ self.reloadData()
+ }
+ let viewController = UIHostingController(rootView: view)
+ viewController.title = PostVisibilityPicker.title
+ viewController.configureDefaultNavigationBarAppearance()
+ navigationController?.pushViewController(viewController, animated: true)
+ }
+
+ private func showWarningPostWillBePublishedAlert() {
+ let alert = UIAlertController(title: nil, message: Strings.warningPostWillBePublishedAlertMessage, preferredStyle: .alert)
+ alert.addAction(UIAlertAction(title: NSLocalizedString("postSettings.ok", value: "OK", comment: "Button OK"), style: .default))
+ present(alert, animated: true)
+ }
+}
+
// MARK: - PostSettingsViewController (Page Attributes)
extension PostSettingsViewController {
@@ -239,4 +281,6 @@ extension PostSettingsViewController {
private enum Strings {
static let errorMessage = NSLocalizedString("postSettings.updateFailedMessage", value: "Failed to update the post settings", comment: "Error message on post/page settings screen")
+
+ static let warningPostWillBePublishedAlertMessage = NSLocalizedString("postSettings.warningPostWillBePublishedAlertMessage", value: "By changing the visibility to 'Private', the post will be published immediately", comment: "An alert message explaning that by changing the visibility to private, the post will be published immediately to your site")
}
diff --git a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m
index 5ae8245cdcb3..bf00ff9681b7 100644
--- a/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m
+++ b/WordPress/Classes/ViewRelated/Post/PostSettingsViewController.m
@@ -68,7 +68,6 @@ @interface PostSettingsViewController () Void
static var title: String { Strings.title }
- init(visibility: PostVisibility, onSubmit: @escaping (Selection) -> Void) {
- self._selection = State(initialValue: visibility)
+ init(selection: Selection, onSubmit: @escaping (Selection) -> Void) {
+ self._selection = State(initialValue: selection)
self.onSubmit = onSubmit
}
@@ -33,11 +37,13 @@ struct PostVisibilityPicker: View {
private func makeRow(for visibility: PostVisibility) -> some View {
Button(action: {
withAnimation {
+ selection.type = visibility
+ selection.password = ""
+
if visibility == .protected {
- isEnteringPassword = true
+ isPasswordFieldFocused = true
} else {
- selection = visibility
- onSubmit(Selection(visibility: visibility, password: nil))
+ onSubmit(selection)
}
}
}, label: {
@@ -47,52 +53,55 @@ struct PostVisibilityPicker: View {
Text(visibility.localizedDetails)
.font(.footnote)
.foregroundStyle(.secondary)
- .opacity(isEnteringPassword ? 0.5 : 1)
+ .opacity(visibility != .protected && isPasswordFieldFocused ? 0.4 : 1)
}
Spacer()
Image(systemName: "checkmark")
.tint(Color(uiColor: .primary))
- .opacity((selection == visibility && !isEnteringPassword) ? 1 : 0)
+ .opacity((selection.type == visibility && !isPasswordFieldFocused) ? 1 : 0)
}
})
.tint(.primary)
- .disabled(isEnteringPassword && visibility != .protected)
+ .disabled(isPasswordFieldFocused && visibility != .protected)
- if visibility == .protected, isEnteringPassword {
+ if visibility == .protected, selection.type == .protected {
enterPasswordRows
}
}
@ViewBuilder
private var enterPasswordRows: some View {
- PasswordField(password: $password)
- .onSubmit(savePassword)
+ PasswordField(password: $selection.password, isFocused: isPasswordFieldFocused)
+ .focused($isPasswordFieldFocused)
+ .onSubmit(buttonSavePasswordTapped)
- HStack {
- Button(Strings.cancel) {
- withAnimation {
- password = ""
- isEnteringPassword = false
+ if isPasswordFieldFocused {
+ HStack {
+ Button(Strings.cancel) {
+ withAnimation {
+ selection.type = .public
+ selection.password = ""
+ }
}
+ .keyboardShortcut(.cancelAction)
+ Spacer()
+ Button(Strings.save, action: buttonSavePasswordTapped)
+ .font(.body.weight(.medium))
+ .disabled(selection.password.trimmingCharacters(in: .whitespaces).isEmpty)
}
- .keyboardShortcut(.cancelAction)
- Spacer()
- Button(Strings.save, action: savePassword)
- .font(.body.weight(.medium))
- .disabled(password.isEmpty)
+ .buttonStyle(.plain)
+ .foregroundStyle(Color(uiColor: .brand))
}
- .buttonStyle(.plain)
- .foregroundStyle(Color(uiColor: .brand))
}
- private func savePassword() {
+ private func buttonSavePasswordTapped() {
withAnimation {
- selection = .protected
- isEnteringPassword = false
+ isPasswordFieldFocused = false
+ selection.password = selection.password.trimmingCharacters(in: .whitespaces)
isDismissing = true
// Let the keyboard dismiss first to avoid janky animation
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(550)) {
- onSubmit(Selection(visibility: .protected, password: password))
+ onSubmit(selection)
}
}
}
@@ -101,13 +110,12 @@ struct PostVisibilityPicker: View {
private struct PasswordField: View {
@Binding var password: String
@State var isSecure = true
- @FocusState private var isFocused: Bool
+ let isFocused: Bool
var body: some View {
HStack {
textField
- .focused($isFocused)
- if !password.isEmpty {
+ if isFocused && !password.isEmpty {
Button(action: { password = "" }) {
Image(systemName: "xmark.circle")
.foregroundStyle(.secondary)
@@ -119,8 +127,8 @@ private struct PasswordField: View {
}
}
.buttonStyle(.plain)
- .onAppear { isFocused = true }
}
+
@ViewBuilder
private var textField: some View {
if isSecure {
@@ -136,8 +144,12 @@ enum PostVisibility: Identifiable, CaseIterable {
case `private`
case protected
+ init(post: AbstractPost) {
+ self.init(status: post.status ?? .draft, password: post.password)
+ }
+
init(status: AbstractPost.Status, password: String?) {
- if password != nil {
+ if let password, !password.isEmpty {
self = .protected
} else if status == .publishPrivate {
self = .private
@@ -163,7 +175,6 @@ enum PostVisibility: Identifiable, CaseIterable {
case .private: NSLocalizedString("postVisibility.private.details", value: "Only visible to site admins and editors", comment: "Details for a 'Private' privacy setting")
}
}
-
}
private enum Strings {
diff --git a/WordPress/Classes/ViewRelated/Post/Prepublishing/PrepublishingViewController.swift b/WordPress/Classes/ViewRelated/Post/Prepublishing/PrepublishingViewController.swift
index 4f4a1a37a0f9..93269aaebe3f 100644
--- a/WordPress/Classes/ViewRelated/Post/Prepublishing/PrepublishingViewController.swift
+++ b/WordPress/Classes/ViewRelated/Post/Prepublishing/PrepublishingViewController.swift
@@ -370,18 +370,17 @@ final class PrepublishingViewController: UIViewController, UITableViewDataSource
// MARK: - Visibility
private func configureVisibilityCell(_ cell: WPTableViewCell) {
- cell.detailTextLabel?.text = viewModel.visibility.localizedTitle
+ cell.detailTextLabel?.text = viewModel.visibility.type.localizedTitle
}
private func didTapVisibilityCell() {
- let view = PostVisibilityPicker(visibility: viewModel.visibility) { [weak self] selection in
+ let view = PostVisibilityPicker(selection: viewModel.visibility) { [weak self] selection in
guard let self else { return }
- self.viewModel.visibility = selection.visibility
- if selection.visibility == .private {
+ self.viewModel.visibility = selection
+ if selection.type == .private {
self.viewModel.publishDate = nil
self.updatePublishButtonLabel()
}
- self.viewModel.password = selection.password
self.reloadData()
self.navigationController?.popViewController(animated: true)
}
@@ -401,7 +400,7 @@ final class PrepublishingViewController: UIViewController, UITableViewDataSource
} else {
cell.detailTextLabel?.text = Strings.immediately
}
- viewModel.visibility == .private ? cell.disable() : cell.enable()
+ viewModel.visibility.type == .private ? cell.disable() : cell.enable()
}
func didTapSchedule(_ indexPath: IndexPath) {
@@ -543,8 +542,7 @@ extension PrepublishingOption {
private final class PrepublishingViewModel {
private let post: AbstractPost
- var visibility: PostVisibility
- var password: String?
+ var visibility: PostVisibilityPicker.Selection
var publishDate: Date?
var publishButtonTitle: String {
@@ -557,8 +555,7 @@ private final class PrepublishingViewModel {
init(post: AbstractPost) {
self.post = post
- self.visibility = PostVisibility(status: post.status ?? .draft, password: post.password)
- self.password = post.password
+ self.visibility = .init(post: post)
// Ask the user to provide the date every time (ignore the obscure WP dateCreated/dateModified logic)
self.publishDate = nil
}
@@ -568,8 +565,8 @@ private final class PrepublishingViewModel {
wpAssert(post.isRevision())
try await coordinator._publish(post.original(), options: .init(
- visibility: visibility,
- password: password,
+ visibility: visibility.type,
+ password: visibility.password,
publishDate: publishDate
))
}
diff --git a/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift b/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift
index c83410bf0356..c5e40da78f4c 100644
--- a/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift
+++ b/WordPress/Classes/ViewRelated/Post/Revisions/RevisionsTableViewController.swift
@@ -1,3 +1,5 @@
+import UIKit
+
class RevisionsTableViewController: UITableViewController {
typealias RevisionLoadedBlock = (AbstractPost) -> Void
@@ -36,7 +38,7 @@ class RevisionsTableViewController: UITableViewController {
required init(post: AbstractPost, onRevisionLoaded: @escaping RevisionLoadedBlock) {
self.post = post
self.onRevisionLoaded = onRevisionLoaded
- super.init(nibName: nil, bundle: nil)
+ super.init(style: .insetGrouped)
}
required init?(coder aDecoder: NSCoder) {
@@ -65,7 +67,7 @@ class RevisionsTableViewController: UITableViewController {
private extension RevisionsTableViewController {
private func setupUI() {
- navigationItem.title = NSLocalizedString("History", comment: "Title of the post history screen")
+ navigationItem.title = Strings.title
let cellNib = UINib(nibName: RevisionsTableViewCell.classNameWithoutNamespaces(),
bundle: Bundle(for: RevisionsTableViewCell.self))
@@ -76,7 +78,9 @@ private extension RevisionsTableViewController {
refreshControl.addTarget(self, action: #selector(refreshRevisions), for: .valueChanged)
self.refreshControl = refreshControl
- tableView.tableFooterView = tableViewFooter
+ if post?.original().isStatus(in: [.draft, .pending]) == false {
+ tableView.tableFooterView = tableViewFooter
+ }
tableView.separatorColor = .divider
WPStyleGuide.configureColors(view: view, tableView: tableView)
@@ -113,7 +117,7 @@ private extension RevisionsTableViewController {
@objc private func refreshRevisions() {
if sectionCount == 0 {
- configureAndDisplayNoResults(title: NoResultsText.loadingTitle,
+ configureAndDisplayNoResults(title: Strings.loading,
accessoryView: NoResultsViewController.loadingAccessoryView())
}
@@ -157,7 +161,7 @@ private extension RevisionsTableViewController {
return
}
- SVProgressHUD.show(withStatus: NSLocalizedString("Loading...", comment: "Text displayed in HUD while a revision post is loading."))
+ SVProgressHUD.show(withStatus: Strings.loading)
let coreDataStack = ContextManager.shared
let postRepository = PostRepository(coreDataStack: coreDataStack)
@@ -229,16 +233,12 @@ extension RevisionsTableViewController: WPTableViewHandlerDelegate {
return Sizes.cellEstimatedRowHeight
}
- override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
- guard let sectionInfo = tableViewHandler.resultsController?.sections?[section],
- let headerView = Bundle.main.loadNibNamed(PageListSectionHeaderView.classNameWithoutNamespaces(),
- owner: nil,
- options: nil)?.first as? PageListSectionHeaderView else {
- return UIView()
+ override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
+ guard let sections = tableViewHandler.resultsController?.sections,
+ sections.indices.contains(section) else {
+ return nil
}
-
- headerView.setTitle(sectionInfo.name)
- return headerView
+ return sections[section].name
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
@@ -287,8 +287,8 @@ extension RevisionsTableViewController: RevisionsView {
case (true, let count) where count == 0:
// When the API call successed but there are no revisions loaded
// This is an edge cas. It shouldn't happen since we open the revisions list only if the post revisions array is not empty.
- configureAndDisplayNoResults(title: NoResultsText.noResultsTitle,
- subtitle: NoResultsText.noResultsSubtitle)
+ configureAndDisplayNoResults(title: Strings.noResultsTitle,
+ subtitle: Strings.noResultsSubtitle)
default:
hideNoResults()
}
@@ -311,7 +311,7 @@ private extension Date {
private static let shortDateFormatter: DateFormatter = {
let formatter = DateFormatter()
- formatter.dateStyle = .short
+ formatter.dateStyle = .medium
formatter.timeStyle = .none
return formatter
}()
@@ -326,10 +326,14 @@ private extension Date {
}
struct NoResultsText {
- static let loadingTitle = NSLocalizedString("Loading history...", comment: "Displayed while a call is loading the history.")
static let reloadButtonTitle = NSLocalizedString("Try again", comment: "Re-load the history again. It appears if the loading call fails.")
- static let noResultsTitle = NSLocalizedString("No history yet", comment: "Displayed when a call is made to load the revisions but there's no result or an error.")
- static let noResultsSubtitle = NSLocalizedString("When you make changes in the editor you'll be able to see the history here", comment: "Displayed when a call is made to load the history but there's no result or an error.")
static let errorTitle = NSLocalizedString("Oops", comment: "Title for the view when there's an error loading the history")
static let errorSubtitle = NSLocalizedString("There was an error loading the history", comment: "Text displayed when there is a failure loading the history.")
}
+
+private enum Strings {
+ static let title = NSLocalizedString("revisions.title", value: "Revisions", comment: "Post revisions list screen title")
+ static let loading = NSLocalizedString("revisions.loadingTitle", value: "Loading…", comment: "Post revisions list screen / loading view title")
+ static let noResultsTitle = NSLocalizedString("revisions.emptyStateTitle", value: "No revisions yet", comment: "Displayed when a call is made to load the revisions but there's no result or an error.")
+ static let noResultsSubtitle = NSLocalizedString("revisions.emptyStateSubtitle", value: "When you make changes in the editor you'll be able to see the revision history here", comment: "Displayed when a call is made to load the history but there's no result or an error.")
+}
diff --git a/WordPress/Classes/ViewRelated/Site Creation/Wizard/SiteCreator.swift b/WordPress/Classes/ViewRelated/Site Creation/Wizard/SiteCreator.swift
index 677e7ebf0e9c..430916f4ae63 100644
--- a/WordPress/Classes/ViewRelated/Site Creation/Wizard/SiteCreator.swift
+++ b/WordPress/Classes/ViewRelated/Site Creation/Wizard/SiteCreator.swift
@@ -71,7 +71,7 @@ final class SiteCreator {
// MARK: - Helper Extensions
-extension String {
+private extension String {
var subdomain: String {
return components(separatedBy: ".").first ?? ""
}
diff --git a/WordPress/WordPress.xcodeproj/project.pbxproj b/WordPress/WordPress.xcodeproj/project.pbxproj
index 05b37b6b7eac..586ae2a4ff51 100644
--- a/WordPress/WordPress.xcodeproj/project.pbxproj
+++ b/WordPress/WordPress.xcodeproj/project.pbxproj
@@ -1496,7 +1496,6 @@
59ECF87B1CB7061D00E68F25 /* PostSharingControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59ECF87A1CB7061D00E68F25 /* PostSharingControllerTests.swift */; };
59FBD5621B5684F300734466 /* ThemeServiceTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 59FBD5611B5684F300734466 /* ThemeServiceTests.m */; };
5D1181E71B4D6DEB003F3084 /* WPStyleGuide+Reader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D1181E61B4D6DEB003F3084 /* WPStyleGuide+Reader.swift */; };
- 5D13FA571AF99C2100F06492 /* PageListSectionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5D13FA561AF99C2100F06492 /* PageListSectionHeaderView.xib */; };
5D146EBB189857ED0068FDC6 /* FeaturedImageViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 5D146EBA189857ED0068FDC6 /* FeaturedImageViewController.m */; };
5D1D04751B7A50B100CDE646 /* Reader.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5D1D04731B7A50B100CDE646 /* Reader.storyboard */; };
5D1D04761B7A50B100CDE646 /* ReaderStreamViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5D1D04741B7A50B100CDE646 /* ReaderStreamViewController.swift */; };
@@ -2397,7 +2396,6 @@
8B92D69627CD51FA001F5371 /* DashboardGhostCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B92D69527CD51FA001F5371 /* DashboardGhostCardCell.swift */; };
8B92D69727CD51FA001F5371 /* DashboardGhostCardCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B92D69527CD51FA001F5371 /* DashboardGhostCardCell.swift */; };
8B93412F257029F60097D0AC /* FilterChipButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B93412E257029F50097D0AC /* FilterChipButton.swift */; };
- 8B93856E22DC08060010BF02 /* PageListSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B93856D22DC08060010BF02 /* PageListSectionHeaderView.swift */; };
8BA125EB27D8F5E4008B779F /* UIView+PinSubviewPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA125EA27D8F5E4008B779F /* UIView+PinSubviewPriority.swift */; };
8BA125EC27D8F5E4008B779F /* UIView+PinSubviewPriority.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8BA125EA27D8F5E4008B779F /* UIView+PinSubviewPriority.swift */; };
8BA77BCB2482C52A00E1EBBF /* ReaderCardDiscoverAttributionView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8BA77BCA2482C52A00E1EBBF /* ReaderCardDiscoverAttributionView.xib */; };
@@ -4223,7 +4221,6 @@
FABB201D2602FC2C00C8785C /* PostStatsTitleCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 98FCFC222231DF43006ECDD4 /* PostStatsTitleCell.xib */; };
FABB20212602FC2C00C8785C /* ReaderDetailToolbar.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8BA77BCC248340CE00E1EBBF /* ReaderDetailToolbar.xib */; };
FABB20222602FC2C00C8785C /* RewindStatusTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4070D75B20E5F55A007CEBDA /* RewindStatusTableViewCell.xib */; };
- FABB20232602FC2C00C8785C /* PageListSectionHeaderView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 5D13FA561AF99C2100F06492 /* PageListSectionHeaderView.xib */; };
FABB20242602FC2C00C8785C /* JetpackScanThreatDetailsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = FA7AA4A625BFE0A9005E7200 /* JetpackScanThreatDetailsViewController.xib */; };
FABB20252602FC2C00C8785C /* CollabsableHeaderFilterCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 469EB16424D8B12700C764CB /* CollabsableHeaderFilterCollectionViewCell.xib */; };
FABB20262602FC2C00C8785C /* ReplyTextView.xib in Resources */ = {isa = PBXBuildFile; fileRef = B54E1DEF1A0A7BAA00807537 /* ReplyTextView.xib */; };
@@ -4505,7 +4502,6 @@
FABB21982602FC2C00C8785C /* UIViewController+Notice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F3EFCA2208E308900268758 /* UIViewController+Notice.swift */; };
FABB21992602FC2C00C8785C /* GutenbergFileUploadProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E0462152566938300EB98EF /* GutenbergFileUploadProcessor.swift */; };
FABB219A2602FC2C00C8785C /* EventLoggingDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F913BB0F24B3C5CE00C19032 /* EventLoggingDataProvider.swift */; };
- FABB219B2602FC2C00C8785C /* PageListSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8B93856D22DC08060010BF02 /* PageListSectionHeaderView.swift */; };
FABB219C2602FC2C00C8785C /* InviteLinks+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E690F6ED25E05D170015A777 /* InviteLinks+CoreDataClass.swift */; };
FABB219D2602FC2C00C8785C /* Page+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59E1D46D1CEF77B500126697 /* Page+CoreDataProperties.swift */; };
FABB219F2602FC2C00C8785C /* RegisterDomainSectionHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 43D74AD520FB5AD5004AD934 /* RegisterDomainSectionHeaderView.swift */; };
@@ -7202,7 +7198,6 @@
59FBD5611B5684F300734466 /* ThemeServiceTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThemeServiceTests.m; sourceTree = ""; };
5C1CEB34870A8BA1ED1E502B /* Pods-WordPressUITests.release-alpha.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressUITests.release-alpha.xcconfig"; path = "../Pods/Target Support Files/Pods-WordPressUITests/Pods-WordPressUITests.release-alpha.xcconfig"; sourceTree = ""; };
5D1181E61B4D6DEB003F3084 /* WPStyleGuide+Reader.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "WPStyleGuide+Reader.swift"; sourceTree = ""; };
- 5D13FA561AF99C2100F06492 /* PageListSectionHeaderView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PageListSectionHeaderView.xib; sourceTree = ""; };
5D146EB9189857ED0068FDC6 /* FeaturedImageViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FeaturedImageViewController.h; sourceTree = ""; usesTabs = 0; };
5D146EBA189857ED0068FDC6 /* FeaturedImageViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = FeaturedImageViewController.m; sourceTree = ""; usesTabs = 0; };
5D1D04731B7A50B100CDE646 /* Reader.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Reader.storyboard; sourceTree = ""; };
@@ -7801,7 +7796,6 @@
8B8FE8562343952B00F9AD2E /* PostAutoUploadMessages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostAutoUploadMessages.swift; sourceTree = ""; };
8B92D69527CD51FA001F5371 /* DashboardGhostCardCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardGhostCardCell.swift; sourceTree = ""; };
8B93412E257029F50097D0AC /* FilterChipButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FilterChipButton.swift; sourceTree = ""; };
- 8B93856D22DC08060010BF02 /* PageListSectionHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PageListSectionHeaderView.swift; sourceTree = ""; };
8B9E15DAF3E1A369E9BE3407 /* Pods-WordPressUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-WordPressUITests.release.xcconfig"; path = "../Pods/Target Support Files/Pods-WordPressUITests/Pods-WordPressUITests.release.xcconfig"; sourceTree = ""; };
8BA125EA27D8F5E4008B779F /* UIView+PinSubviewPriority.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+PinSubviewPriority.swift"; sourceTree = ""; };
8BA77BCA2482C52A00E1EBBF /* ReaderCardDiscoverAttributionView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ReaderCardDiscoverAttributionView.xib; sourceTree = ""; };
@@ -12987,8 +12981,6 @@
children = (
0C700B852AE1E1300085C2EE /* PageListCell.swift */,
0C700B882AE1E1940085C2EE /* PageListItemViewModel.swift */,
- 8B93856D22DC08060010BF02 /* PageListSectionHeaderView.swift */,
- 5D13FA561AF99C2100F06492 /* PageListSectionHeaderView.xib */,
834A49D12A0C23A90042ED3D /* TemplatePageTableViewCell.swift */,
);
name = Views;
@@ -19724,7 +19716,6 @@
FE015BB12ADA002400F50D7F /* ReaderTopicsNewCardCell.xib in Resources */,
8BA77BCD248340CE00E1EBBF /* ReaderDetailToolbar.xib in Resources */,
4070D75C20E5F55A007CEBDA /* RewindStatusTableViewCell.xib in Resources */,
- 5D13FA571AF99C2100F06492 /* PageListSectionHeaderView.xib in Resources */,
1761F18D26209AEE000815EF /* hot-pink-icon-app-76x76.png in Resources */,
FA7AA4A725BFE0A9005E7200 /* JetpackScanThreatDetailsViewController.xib in Resources */,
469EB16624D8B12700C764CB /* CollabsableHeaderFilterCollectionViewCell.xib in Resources */,
@@ -20261,7 +20252,6 @@
F46597FD28E66A1100D5F49A /* white-on-black-icon-app-60@2x.png in Resources */,
FABB20212602FC2C00C8785C /* ReaderDetailToolbar.xib in Resources */,
FABB20222602FC2C00C8785C /* RewindStatusTableViewCell.xib in Resources */,
- FABB20232602FC2C00C8785C /* PageListSectionHeaderView.xib in Resources */,
8091019629078CFE00FCB4EA /* JetpackFullscreenOverlayViewController.xib in Resources */,
98DCF4A8275945E00008630F /* ReaderDetailNoCommentCell.xib in Resources */,
FABB20242602FC2C00C8785C /* JetpackScanThreatDetailsViewController.xib in Resources */,
@@ -21779,7 +21769,6 @@
C3B554512965C32A00A04753 /* MenusViewController+JetpackBannerViewController.swift in Sources */,
1E0462162566938300EB98EF /* GutenbergFileUploadProcessor.swift in Sources */,
F913BB1024B3C5CE00C19032 /* EventLoggingDataProvider.swift in Sources */,
- 8B93856E22DC08060010BF02 /* PageListSectionHeaderView.swift in Sources */,
E690F6EF25E05D180015A777 /* InviteLinks+CoreDataClass.swift in Sources */,
59E1D46F1CEF77B500126697 /* Page+CoreDataProperties.swift in Sources */,
C373D6E728045281008F8C26 /* SiteIntentData.swift in Sources */,
@@ -24688,7 +24677,6 @@
F4CBE3D7292597E3004FFBB6 /* SupportTableViewControllerConfiguration.swift in Sources */,
FABB21992602FC2C00C8785C /* GutenbergFileUploadProcessor.swift in Sources */,
FABB219A2602FC2C00C8785C /* EventLoggingDataProvider.swift in Sources */,
- FABB219B2602FC2C00C8785C /* PageListSectionHeaderView.swift in Sources */,
FABB219C2602FC2C00C8785C /* InviteLinks+CoreDataClass.swift in Sources */,
809101992908DE8500FCB4EA /* JetpackFullscreenOverlayViewModel.swift in Sources */,
FABB219D2602FC2C00C8785C /* Page+CoreDataProperties.swift in Sources */,