Skip to content

Commit 9e1f1e0

Browse files
authored
Post List: Fix Visual Issues (#24647)
* Fix Posts loading state * Refinit post list look * Improve how title and excerpt are shown * Update release notes * Fix loading state
1 parent 9119f09 commit 9e1f1e0

12 files changed

+115
-65
lines changed

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
* [*] Add search to “Jetpack Activity List” and display actors and dates [#24597]
88
* [*] Fix an issue with content in "Restore" and "Download Backup" flows covering the navigation bar [#24597]
99
* [*] Show when the downloadable backup expires in Backup List [#24597]
10+
* [*] Fix a couple of visual issues in Post List [#24647]
1011

1112
25.9
1213
-----

Sources/WordPressData/Swift/Post.swift

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,14 +231,14 @@ public class Post: AbstractPost {
231231
if let preview = PostPreviewCache.shared.excerpt[excerpt] {
232232
return preview
233233
}
234-
let preview = excerpt.makePlainText()
234+
let preview = excerpt.makePlainText().withCollapsedNewlines()
235235
PostPreviewCache.shared.excerpt[excerpt] = preview
236236
return preview
237237
} else if let content {
238238
if let preview = PostPreviewCache.shared.content[content] {
239239
return preview
240240
}
241-
let preview = content.summarized()
241+
let preview = content.summarized().withCollapsedNewlines()
242242
PostPreviewCache.shared.content[content] = preview
243243
return preview
244244
} else {
@@ -260,6 +260,13 @@ public class Post: AbstractPost {
260260
}
261261
}
262262

263+
private extension String {
264+
// Normalize newlines by collapsing multiple occurrences of newlines to a single newline
265+
func withCollapsedNewlines() -> String {
266+
replacingOccurrences(of: "[\n]{2,}", with: "\n", options: .regularExpression)
267+
}
268+
}
269+
263270
private final class PostPreviewCache {
264271
static let shared = PostPreviewCache()
265272

WordPress/Classes/ViewRelated/Media/NoResultsViewController+MediaLibrary.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ extension NoResultsViewController {
1010
}
1111

1212
@objc func configureForFetching() {
13-
configure(title: LocalizedText.fetchingTitle, accessoryView: NoResultsViewController.loadingAccessoryView())
13+
configure(title: "", accessoryView: NoResultsViewController.loadingAccessoryView())
1414
view.layoutIfNeeded()
1515
}
1616

WordPress/Classes/ViewRelated/Pages/Controllers/PageListViewController.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ private extension PageListViewController {
471471

472472
func noResultsTitle() -> String {
473473
if syncHelper.isSyncing == true {
474-
return NoResultsText.fetchingTitle
474+
return ""
475475
}
476476
return noResultsFilteredTitle()
477477
}
@@ -493,7 +493,6 @@ private extension PageListViewController {
493493
}
494494

495495
struct NoResultsText {
496-
static let fetchingTitle = NSLocalizedString("Fetching pages...", comment: "A brief prompt shown when the reader is empty, letting the user know the app is currently fetching new pages.")
497496
static let noDraftsTitle = NSLocalizedString("You don't have any draft pages", comment: "Displayed when the user views drafts in the pages list and there are no pages")
498497
static let noScheduledTitle = NSLocalizedString("You don't have any scheduled pages", comment: "Displayed when the user views scheduled pages in the pages list and there are no pages")
499498
static let noTrashedTitle = NSLocalizedString("You don't have any trashed pages", comment: "Displayed when the user views trashed in the pages list and there are no pages")

WordPress/Classes/ViewRelated/Pages/Views/PageListCell.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,16 @@ final class PageListCell: UITableViewCell, AbstractPostListCell, PostSearchResul
2121

2222
// MARK: - PostSearchResultCell
2323

24-
var attributedText: NSAttributedString? {
24+
var titleAttributedText: NSAttributedString? {
2525
get { titleLabel.attributedText }
2626
set { titleLabel.attributedText = newValue }
2727
}
2828

29+
var excerptAttributedText: NSAttributedString? {
30+
get { nil } // Pages don't have excerpts in the list
31+
set { } // Pages don't have excerpts in the list
32+
}
33+
2934
// MARK: AbstractPostListCell
3035

3136
var post: AbstractPost? { viewModel?.page }

WordPress/Classes/ViewRelated/Post/Controllers/AbstractPostListViewController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ class AbstractPostListViewController: UIViewController,
300300
if noResultsViewController.view.isDescendant(of: tableView) == false {
301301
self.addChild(noResultsViewController)
302302
tableView.addSubview(noResultsViewController.view)
303-
noResultsViewController.view.frame = tableView.frame.offsetBy(dx: 0, dy: -view.safeAreaInsets.top + 40)
303+
noResultsViewController.view.pinEdges(to: view.safeAreaLayoutGuide)
304304
noResultsViewController.didMove(toParent: self)
305305
}
306306

WordPress/Classes/ViewRelated/Post/Controllers/PostListViewController.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ private extension PostListViewController {
284284

285285
func noResultsTitle() -> String {
286286
if syncHelper.isSyncing == true {
287-
return NoResultsText.fetchingTitle
287+
return ""
288288
}
289289
return noResultsFilteredTitle()
290290
}
@@ -306,7 +306,6 @@ private extension PostListViewController {
306306
}
307307

308308
struct NoResultsText {
309-
static let fetchingTitle = NSLocalizedString("Fetching posts...", comment: "A brief prompt shown when the reader is empty, letting the user know the app is currently fetching new posts.")
310309
static let noDraftsTitle = NSLocalizedString("You don't have any draft posts", comment: "Displayed when the user views drafts in the posts list and there are no posts")
311310
static let noScheduledTitle = NSLocalizedString("You don't have any scheduled posts", comment: "Displayed when the user views scheduled posts in the posts list and there are no posts")
312311
static let noTrashedTitle = NSLocalizedString("You don't have any trashed posts", comment: "Displayed when the user views trashed in the posts list and there are no posts")

WordPress/Classes/ViewRelated/Post/Search/PostSearchViewController.swift

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -235,10 +235,19 @@ final class PostSearchViewController: UIViewController, UITableViewDelegate, UIS
235235
for cell in cells {
236236
guard let cell = cell as? PostSearchResultCell else { continue }
237237

238-
assert(cell.attributedText != nil)
239-
let string = NSMutableAttributedString(attributedString: cell.attributedText ?? .init())
240-
PostSearchViewModel.highlight(terms: terms, in: string)
241-
cell.attributedText = string
238+
// Highlight title
239+
if let titleText = cell.titleAttributedText {
240+
let titleString = NSMutableAttributedString(attributedString: titleText)
241+
PostSearchViewModel.highlight(terms: terms, in: titleString)
242+
cell.titleAttributedText = titleString
243+
}
244+
245+
// Highlight excerpt
246+
if let excerptText = cell.excerptAttributedText {
247+
let excerptString = NSMutableAttributedString(attributedString: excerptText)
248+
PostSearchViewModel.highlight(terms: terms, in: excerptString)
249+
cell.excerptAttributedText = excerptString
250+
}
242251
}
243252
}
244253
}
@@ -250,5 +259,6 @@ private enum Constants {
250259
}
251260

252261
protocol PostSearchResultCell: AnyObject {
253-
var attributedText: NSAttributedString? { get set }
262+
var titleAttributedText: NSAttributedString? { get set }
263+
var excerptAttributedText: NSAttributedString? { get set }
254264
}

WordPress/Classes/ViewRelated/Post/Views/AbstractPostHelper.swift

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,15 @@ enum AbstractPostHelper {
4040
for (badge, color) in badges {
4141
if string.length > 0 {
4242
string.append(NSAttributedString(string: " · ", attributes: [
43-
.foregroundColor: UIColor.secondaryLabel
43+
.foregroundColor: UIColor.secondaryLabel,
44+
.font: UIFont.preferredFont(forTextStyle: .footnote).withWeight(.bold)
4445
]))
4546
}
4647
string.append(NSAttributedString(string: badge, attributes: [
47-
.foregroundColor: color ?? UIColor.secondaryLabel
48+
.foregroundColor: color ?? UIColor.secondaryLabel,
49+
.font: UIFont.preferredFont(forTextStyle: .footnote)
4850
]))
4951
}
50-
string.addAttribute(.font, value: WPStyleGuide.fontForTextStyle(.footnote), range: NSRange(location: 0, length: string.length))
5152
return string
5253
}
5354
}

WordPress/Classes/ViewRelated/Post/Views/PostListCell.swift

Lines changed: 39 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ final class PostListCell: UITableViewCell, AbstractPostListCell, PostSearchResul
2626

2727
private let headerView = PostListHeaderView()
2828
private let ellipsisButton = UIButton(type: .system)
29-
private let contentLabel = UILabel()
29+
private let titleLabel = UILabel()
30+
private let excerptLabel = UILabel()
3031
private let featuredImageView = AsyncImageView()
3132
private let statusLabel = UILabel()
3233

@@ -36,9 +37,14 @@ final class PostListCell: UITableViewCell, AbstractPostListCell, PostSearchResul
3637

3738
// MARK: - PostSearchResultCell
3839

39-
var attributedText: NSAttributedString? {
40-
get { contentLabel.attributedText }
41-
set { contentLabel.attributedText = newValue }
40+
var titleAttributedText: NSAttributedString? {
41+
get { titleLabel.attributedText }
42+
set { titleLabel.attributedText = newValue }
43+
}
44+
45+
var excerptAttributedText: NSAttributedString? {
46+
get { excerptLabel.attributedText }
47+
set { excerptLabel.attributedText = newValue }
4248
}
4349

4450
// MARK: AbstractPostListCell
@@ -73,7 +79,8 @@ final class PostListCell: UITableViewCell, AbstractPostListCell, PostSearchResul
7379

7480
private func _configure(with viewModel: PostListItemViewModel, delegate: InteractivePostViewDelegate? = nil) {
7581
headerView.configure(with: viewModel)
76-
contentLabel.attributedText = viewModel.content
82+
titleLabel.attributedText = viewModel.title
83+
excerptLabel.attributedText = viewModel.excerpt
7784

7885
featuredImageView.isHidden = viewModel.imageURL == nil
7986
featuredImageView.layer.opacity = viewModel.syncStateViewModel.isEditable ? 1 : 0.25
@@ -111,15 +118,20 @@ final class PostListCell: UITableViewCell, AbstractPostListCell, PostSearchResul
111118
// MARK: - Setup
112119

113120
private func setupViews() {
114-
setupContentLabel()
121+
setupTitleLabel()
122+
setupExcerptLabel()
115123
setupFeaturedImageView()
116124
setupStatusLabel()
117125

126+
let textStackView = UIStackView(arrangedSubviews: [titleLabel, excerptLabel])
127+
textStackView.axis = .vertical
128+
textStackView.spacing = 3
129+
118130
contentStackView.addArrangedSubviews([
119-
contentLabel,
131+
textStackView,
120132
featuredImageView
121133
])
122-
contentStackView.spacing = 16
134+
contentStackView.spacing = 8
123135
contentStackView.alignment = .top
124136

125137
mainStackView.addArrangedSubviews([
@@ -128,24 +140,32 @@ final class PostListCell: UITableViewCell, AbstractPostListCell, PostSearchResul
128140
statusLabel
129141
])
130142
mainStackView.spacing = 4
143+
mainStackView.setCustomSpacing(5, after: headerView)
131144
contentView.addSubview(mainStackView)
132-
mainStackView.pinEdges(to: contentView.layoutMarginsGuide)
145+
mainStackView.pinEdges(to: contentView.layoutMarginsGuide, insets: UIEdgeInsets(top: 0, left: 0, bottom: 2, right: -2))
133146

134147
// It is added last to ensure it's tappable
135148
setupEllipsisButton()
136149
}
137150

138-
private func setupContentLabel() {
139-
contentLabel.adjustsFontForContentSizeCategory = true
140-
contentLabel.numberOfLines = 3
141-
contentLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
151+
private func setupTitleLabel() {
152+
titleLabel.adjustsFontForContentSizeCategory = true
153+
titleLabel.numberOfLines = 2
154+
titleLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
155+
}
156+
157+
private func setupExcerptLabel() {
158+
excerptLabel.adjustsFontForContentSizeCategory = true
159+
excerptLabel.numberOfLines = 2
160+
excerptLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
142161
}
143162

144163
private func setupFeaturedImageView() {
145164
featuredImageView.contentMode = .scaleAspectFill
146165
featuredImageView.layer.masksToBounds = true
147-
featuredImageView.layer.cornerRadius = 5
166+
featuredImageView.layer.cornerRadius = 8
148167
featuredImageView.setContentCompressionResistancePriority(.defaultHigh, for: .vertical)
168+
featuredImageView.configuration.isErrorViewEnabled = false
149169

150170
NSLayoutConstraint.activate([
151171
featuredImageView.widthAnchor.constraint(equalToConstant: Constants.imageSize.width),
@@ -161,11 +181,12 @@ final class PostListCell: UITableViewCell, AbstractPostListCell, PostSearchResul
161181

162182
private func setupEllipsisButton() {
163183
ellipsisButton.setImage(UIImage(named: "more-horizontal-mobile"), for: .normal)
164-
ellipsisButton.tintColor = .secondaryLabel
184+
ellipsisButton.tintColor = .tertiaryLabel
165185

186+
/// warning: See `spacer` in `PostListHeaderView` to understand the layout
166187
NSLayoutConstraint.activate([
167-
ellipsisButton.heightAnchor.constraint(equalToConstant: 44),
168-
ellipsisButton.widthAnchor.constraint(equalToConstant: 54)
188+
ellipsisButton.heightAnchor.constraint(equalToConstant: 40),
189+
ellipsisButton.widthAnchor.constraint(equalToConstant: 56)
169190
])
170191

171192
contentView.addSubview(ellipsisButton)
@@ -174,5 +195,5 @@ final class PostListCell: UITableViewCell, AbstractPostListCell, PostSearchResul
174195
}
175196

176197
private enum Constants {
177-
static let imageSize = CGSize(width: 64, height: 64)
198+
static let imageSize = CGSize(width: 54, height: 54)
178199
}

0 commit comments

Comments
 (0)