Skip to content

Commit

Permalink
Merge pull request #21804 from wordpress-mobile/task/21712-update-pos…
Browse files Browse the repository at this point in the history
…t-cell-design

Posts & Pages: Update posts cell design
  • Loading branch information
momo-ozawa authored Oct 18, 2023
2 parents 23000ed + a4d2c1c commit 4fbcc0c
Show file tree
Hide file tree
Showing 6 changed files with 292 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ class AbstractPostListViewController: UIViewController,
view.pinSubviewToAllEdges(tableViewController.view)
tableViewController.didMove(toParent: self)

tableView.backgroundColor = .white
tableView.backgroundColor = .systemBackground
tableView.sectionHeaderTopPadding = 0
tableView.refreshControl = refreshControl
refreshControl.addTarget(self, action: #selector(refresh), for: .valueChanged)
Expand Down
140 changes: 140 additions & 0 deletions WordPress/Classes/ViewRelated/Post/PostListCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import Foundation
import UIKit

final class PostListCell: UITableViewCell, Reusable {

// MARK: - Views

private lazy var mainStackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
return stackView
}()

private lazy var contentStackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .horizontal
return stackView
}()

private let headerView = PostListHeaderView()
private let titleAndSnippetLabel = UILabel()
private let featuredImageView = CachedAnimatedImageView()
private let statusLabel = UILabel()

// MARK: - Properties

private lazy var imageLoader = ImageLoader(imageView: featuredImageView)

// MARK: - Initializers

override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setupViews()
}

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

// MARK: - Public

func configure(with viewModel: PostListItemViewModel) {
headerView.configure(with: viewModel)

configureTitleAndSnippet(with: viewModel)

imageLoader.prepareForReuse()
featuredImageView.isHidden = viewModel.imageURL == nil
if let imageURL = viewModel.imageURL {
let host = MediaHost(with: viewModel.post) { error in
WordPressAppDelegate.crashLogging?.logError(error)
}
let preferredSize = CGSize(width: 44, height: 44)
imageLoader.loadImage(with: imageURL, from: host, preferredSize: preferredSize)
}

statusLabel.text = viewModel.status
statusLabel.textColor = viewModel.statusColor
statusLabel.isHidden = viewModel.status.isEmpty
}

private func configureTitleAndSnippet(with viewModel: PostListItemViewModel) {
var titleAndSnippetString = NSMutableAttributedString()

if let title = viewModel.title, !title.isEmpty {
let attributes: [NSAttributedString.Key: Any] = [
.font: WPStyleGuide.fontForTextStyle(.callout, fontWeight: .semibold),
.foregroundColor: UIColor.text
]
let titleAttributedString = NSAttributedString(string: "\(title)\n", attributes: attributes)
titleAndSnippetString.append(titleAttributedString)
}

if let snippet = viewModel.snippet, !snippet.isEmpty {
let attributes: [NSAttributedString.Key: Any] = [
.font: WPStyleGuide.fontForTextStyle(.footnote, fontWeight: .regular),
.foregroundColor: UIColor.textSubtle
]
let snippetAttributedString = NSAttributedString(string: snippet, attributes: attributes)
titleAndSnippetString.append(snippetAttributedString)
}

titleAndSnippetLabel.attributedText = titleAndSnippetString
}

// MARK: - Setup

private func setupViews() {
setupTitleAndSnippetLabel()
setupFeaturedImageView()
setupStatusLabel()

contentStackView.translatesAutoresizingMaskIntoConstraints = false
contentStackView.addArrangedSubviews([
titleAndSnippetLabel,
featuredImageView
])
contentStackView.spacing = 16
contentStackView.alignment = .top

mainStackView.translatesAutoresizingMaskIntoConstraints = false
mainStackView.addArrangedSubviews([
headerView,
contentStackView,
statusLabel
])
mainStackView.spacing = 4
mainStackView.isLayoutMarginsRelativeArrangement = true
mainStackView.directionalLayoutMargins = NSDirectionalEdgeInsets(top: 12, leading: 16, bottom: 12, trailing: 16)

contentView.addSubview(mainStackView)
contentView.pinSubviewToAllEdges(mainStackView)
contentView.backgroundColor = .systemBackground
}

private func setupTitleAndSnippetLabel() {
titleAndSnippetLabel.translatesAutoresizingMaskIntoConstraints = false
titleAndSnippetLabel.adjustsFontForContentSizeCategory = true
titleAndSnippetLabel.numberOfLines = 3
}

private func setupFeaturedImageView() {
featuredImageView.translatesAutoresizingMaskIntoConstraints = false
featuredImageView.contentMode = .scaleAspectFill
featuredImageView.layer.masksToBounds = true
featuredImageView.layer.cornerRadius = 5

NSLayoutConstraint.activate([
featuredImageView.widthAnchor.constraint(equalToConstant: 64),
featuredImageView.heightAnchor.constraint(equalToConstant: 64),
])
}

private func setupStatusLabel() {
statusLabel.translatesAutoresizingMaskIntoConstraints = false
statusLabel.adjustsFontForContentSizeCategory = true
statusLabel.numberOfLines = 1
statusLabel.font = WPStyleGuide.fontForTextStyle(.footnote, fontWeight: .regular)
}
}
99 changes: 99 additions & 0 deletions WordPress/Classes/ViewRelated/Post/PostListHeaderView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import UIKit

final class PostListHeaderView: UIView {

// MARK: - Views

private lazy var mainStackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .horizontal
return stackView
}()

private lazy var labelsStackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .horizontal
return stackView
}()

private let dateLabel = UILabel()
private let dotLabel = UILabel()
private let authorLabel = UILabel()
private let ellipsisButton = UIButton(type: .custom)

// MARK: - Initializer

override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}

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

// MARK: - Public

func configure(with viewModel: PostListItemViewModel) {
authorLabel.text = viewModel.author
dateLabel.text = viewModel.date
}

// MARK: - Setup

private func setupView() {
setupLabelStackView()
setupEllipsisButton()

mainStackView.translatesAutoresizingMaskIntoConstraints = false
mainStackView.addArrangedSubviews([
labelsStackView,
ellipsisButton
])
mainStackView.spacing = 16

addSubview(mainStackView)
pinSubviewToAllEdges(mainStackView)
}

private func setupLabelStackView() {
dotLabel.text = "\u{2022}"

let labels = [
dateLabel,
dotLabel,
authorLabel
]

labels.enumerated().forEach { (index, label) in
label.translatesAutoresizingMaskIntoConstraints = false
label.adjustsFontForContentSizeCategory = true
label.numberOfLines = 1
label.font = WPStyleGuide.fontForTextStyle(.footnote, fontWeight: .regular)
label.textColor = .textSubtle

if index < labels.count - 1 {
label.setContentHuggingPriority(.defaultHigh, for: .horizontal)
}
}

labelsStackView.translatesAutoresizingMaskIntoConstraints = false
labelsStackView.addArrangedSubviews(labels)
labelsStackView.spacing = 2
}

private func setupEllipsisButton() {
ellipsisButton.translatesAutoresizingMaskIntoConstraints = false
ellipsisButton.setImage(UIImage(named: "more-horizontal-mobile"), for: .normal)
ellipsisButton.tintColor = .listIcon
ellipsisButton.addTarget(self, action: #selector(ellipsisButtonTapped), for: .touchUpInside)

NSLayoutConstraint.activate([
ellipsisButton.widthAnchor.constraint(equalToConstant: 24)
])
}

@objc private func ellipsisButtonTapped() {
// TODO
}
}
33 changes: 33 additions & 0 deletions WordPress/Classes/ViewRelated/Post/PostListItemViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import Foundation

struct PostListItemViewModel {
let post: Post
let title: String?
let snippet: String?
let imageURL: URL?
let date: String?
let accessibilityIdentifier: String?

private var statusViewModel: PostCardStatusViewModel { .init(post: post) }

var status: String { statusViewModel.statusAndBadges(separatedBy: " · ")}
var statusColor: UIColor { statusViewModel.statusColor }
var author: String { statusViewModel.author }

init(post: Post) {
self.post = post
self.title = post.titleForDisplay()
self.snippet = post.contentPreviewForDisplay()
self.imageURL = post.featuredImageURL
self.date = post.displayDate()?.capitalizeFirstWord
self.accessibilityIdentifier = post.slugForDisplay()
}
}

private extension String {
var capitalizeFirstWord: String {
let firstLetter = self.prefix(1).capitalized
let remainingLetters = self.dropFirst()
return firstLetter + remainingLetters
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,8 @@ class PostListViewController: AbstractPostListViewController, UIViewControllerRe

override func configureTableView() {
tableView.accessibilityIdentifier = "PostsTable"
tableView.separatorStyle = .none
tableView.estimatedRowHeight = postCardEstimatedRowHeight
tableView.separatorStyle = .singleLine
tableView.rowHeight = UITableView.automaticDimension
tableView.separatorStyle = .none

let bundle = Bundle.main

Expand Down
19 changes: 18 additions & 1 deletion WordPress/WordPress.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -5406,6 +5406,12 @@
FAC1B82729B1F1EE00E0C542 /* BlazePostPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC1B82629B1F1EE00E0C542 /* BlazePostPreviewView.swift */; };
FAC1B82829B1F1EE00E0C542 /* BlazePostPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAC1B82629B1F1EE00E0C542 /* BlazePostPreviewView.swift */; };
FACB36F11C5C2BF800C6DF4E /* ThemeWebNavigationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = FACB36F01C5C2BF800C6DF4E /* ThemeWebNavigationDelegate.swift */; };
FACF66CA2ADD4703008C3E13 /* PostListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = FACF66C92ADD4703008C3E13 /* PostListCell.swift */; };
FACF66CB2ADD4703008C3E13 /* PostListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = FACF66C92ADD4703008C3E13 /* PostListCell.swift */; };
FACF66CD2ADD645C008C3E13 /* PostListHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FACF66CC2ADD645C008C3E13 /* PostListHeaderView.swift */; };
FACF66CE2ADD645C008C3E13 /* PostListHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FACF66CC2ADD645C008C3E13 /* PostListHeaderView.swift */; };
FACF66D02ADD6CD8008C3E13 /* PostListItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FACF66CF2ADD6CD8008C3E13 /* PostListItemViewModel.swift */; };
FACF66D12ADD6CD8008C3E13 /* PostListItemViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = FACF66CF2ADD6CD8008C3E13 /* PostListItemViewModel.swift */; };
FAD1263C2A0CF2F50004E24C /* String+NonbreakingSpace.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAD1263B2A0CF2F50004E24C /* String+NonbreakingSpace.swift */; };
FAD1263D2A0CF2F50004E24C /* String+NonbreakingSpace.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAD1263B2A0CF2F50004E24C /* String+NonbreakingSpace.swift */; };
FAD2538F26116A1600EDAF88 /* AppStyleGuide.swift in Sources */ = {isa = PBXBuildFile; fileRef = FAD2538E26116A1600EDAF88 /* AppStyleGuide.swift */; };
Expand Down Expand Up @@ -9289,6 +9295,9 @@
FAC1B81D29B0C2AC00E0C542 /* BlazeOverlayViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlazeOverlayViewModel.swift; sourceTree = "<group>"; };
FAC1B82629B1F1EE00E0C542 /* BlazePostPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BlazePostPreviewView.swift; sourceTree = "<group>"; };
FACB36F01C5C2BF800C6DF4E /* ThemeWebNavigationDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeWebNavigationDelegate.swift; sourceTree = "<group>"; };
FACF66C92ADD4703008C3E13 /* PostListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostListCell.swift; sourceTree = "<group>"; };
FACF66CC2ADD645C008C3E13 /* PostListHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostListHeaderView.swift; sourceTree = "<group>"; };
FACF66CF2ADD6CD8008C3E13 /* PostListItemViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PostListItemViewModel.swift; sourceTree = "<group>"; };
FAD1263B2A0CF2F50004E24C /* String+NonbreakingSpace.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+NonbreakingSpace.swift"; sourceTree = "<group>"; };
FAD2538E26116A1600EDAF88 /* AppStyleGuide.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStyleGuide.swift; sourceTree = "<group>"; };
FAD2544126116CEA00EDAF88 /* AppStyleGuide.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppStyleGuide.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -12525,6 +12534,9 @@
57AA8490228715E700D3C2A2 /* PostCardCell.xib */,
577C2AB322943FEC00AD1F03 /* PostCompactCell.swift */,
577C2AB52294401800AD1F03 /* PostCompactCell.xib */,
FACF66C92ADD4703008C3E13 /* PostListCell.swift */,
FACF66CC2ADD645C008C3E13 /* PostListHeaderView.swift */,
FACF66CF2ADD6CD8008C3E13 /* PostListItemViewModel.swift */,
);
name = Views;
sourceTree = "<group>";
Expand Down Expand Up @@ -21982,6 +21994,7 @@
E62CE58E26B1D14200C9D147 /* AccountService+Cookies.swift in Sources */,
3F4A4C232AD3FA2E00DE5DF8 /* MySiteViewModel.swift in Sources */,
C81CCD7F243BF7A600A83E27 /* TenorMediaGroup.swift in Sources */,
FACF66D02ADD6CD8008C3E13 /* PostListItemViewModel.swift in Sources */,
B0F2EFBF259378E600C7EB6D /* SiteSuggestionService.swift in Sources */,
4388FF0020A4E19C00783948 /* NotificationsViewController+PushPrimer.swift in Sources */,
800035BD291DD0D7007D2D26 /* JetpackFullscreenOverlayGeneralViewModel+Analytics.swift in Sources */,
Expand Down Expand Up @@ -22112,6 +22125,7 @@
2F08ECFC2283A4FB000F8E11 /* PostService+UnattachedMedia.swift in Sources */,
E61084BF1B9B47BA008050C5 /* ReaderDefaultTopic.swift in Sources */,
8BB185D624B66FE600A4CCE8 /* ReaderCard+CoreDataClass.swift in Sources */,
FACF66CD2ADD645C008C3E13 /* PostListHeaderView.swift in Sources */,
D816C1F220E0894D00C4D82F /* ReplyToComment.swift in Sources */,
E1BB92321FDAAFFA00F2D817 /* TextWithAccessoryButtonCell.swift in Sources */,
C81CCD70243AFAE600A83E27 /* TenorResponse.swift in Sources */,
Expand Down Expand Up @@ -22670,6 +22684,7 @@
93CDC72126CD342900C8A3A8 /* DestructiveAlertHelper.swift in Sources */,
982D99FE26F922C100AA794C /* InlineEditableMultiLineCell.swift in Sources */,
B5FF3BE71CAD881100C1D597 /* ImageCropOverlayView.swift in Sources */,
FACF66CA2ADD4703008C3E13 /* PostListCell.swift in Sources */,
F5E032DB24088F44003AF350 /* UIView+SpringAnimations.swift in Sources */,
E6A338501BB0A70F00371587 /* ReaderGapMarkerCell.swift in Sources */,
98BDFF6B20D0732900C72C58 /* SupportTableViewController+Activity.swift in Sources */,
Expand Down Expand Up @@ -23860,6 +23875,7 @@
FABB21332602FC2C00C8785C /* TwoColumnCell.swift in Sources */,
C79C308326EA9A2300E88514 /* ReferrerDetailsCell.swift in Sources */,
FABB21342602FC2C00C8785C /* PostSettingsViewController.m in Sources */,
FACF66CB2ADD4703008C3E13 /* PostListCell.swift in Sources */,
FABB21352602FC2C00C8785C /* MenuItem.m in Sources */,
FABB21362602FC2C00C8785C /* WPRichTextFormatter.swift in Sources */,
175F99B62625FDE100F2687E /* FancyAlertViewController+AppIcons.swift in Sources */,
Expand Down Expand Up @@ -24945,6 +24961,7 @@
FABB24552602FC2C00C8785C /* WPStyleGuide+Suggestions.m in Sources */,
FABB24562602FC2C00C8785C /* SiteStatsInsightsViewModel.swift in Sources */,
0C71959C2A3CA582002EA18C /* SiteSettingsRelatedPostsView.swift in Sources */,
FACF66CE2ADD645C008C3E13 /* PostListHeaderView.swift in Sources */,
FABB24572602FC2C00C8785C /* CheckmarkTableViewCell.swift in Sources */,
FABB24582602FC2C00C8785C /* ReaderManageScenePresenter.swift in Sources */,
FABB24592602FC2C00C8785C /* MediaService.swift in Sources */,
Expand Down Expand Up @@ -25380,6 +25397,7 @@
982D26202788DDF200A41286 /* ReaderCommentsFollowPresenter.swift in Sources */,
FABB258A2602FC2C00C8785C /* BlogAuthor.swift in Sources */,
FABB258B2602FC2C00C8785C /* Blog+BlogAuthors.swift in Sources */,
FACF66D12ADD6CD8008C3E13 /* PostListItemViewModel.swift in Sources */,
FABB258C2602FC2C00C8785C /* ActivityContentStyles.swift in Sources */,
FABB258D2602FC2C00C8785C /* CategorySectionTableViewCell.swift in Sources */,
FAE4CA692732C094003BFDFE /* QuickStartPromptViewController.swift in Sources */,
Expand Down Expand Up @@ -25542,7 +25560,6 @@
C7124E4F2638528F00929318 /* JetpackPrologueViewController.swift in Sources */,
FE341706275FA157005D5CA7 /* RichCommentContentRenderer.swift in Sources */,
08240C2F2AB8A2DD00E7AEA8 /* DomainListCard.swift in Sources */,
01E61E5B29F03DEC002E544E /* DashboardDomainsCardSearchView.swift in Sources */,
FABB26022602FC2C00C8785C /* PostingActivityMonth.swift in Sources */,
0107E15D28FFE99300DE87DB /* WidgetConfiguration.swift in Sources */,
FABB26042602FC2C00C8785C /* ReaderTabItem.swift in Sources */,
Expand Down

0 comments on commit 4fbcc0c

Please sign in to comment.